Go语言 如何在单元测试中测试函数的输出(stdout/stderr)

mwngjboj  于 2023-09-28  发布在  Go
关注(0)|答案(4)|浏览(123)

我想测试一个简单的函数:

func (t *Thing) print(min_verbosity int, message string) {
    if t.verbosity >= minv {
        fmt.Print(message)
    }
}

但是如何测试函数实际发送到标准输出的内容呢?Test::Output在Perl中做了我想做的事情。我知道我可以编写自己的样板文件来在Go中做同样的事情(如here所述):

orig = os.Stdout
r,w,_ = os.Pipe()
thing.print("Some message")
var buf bytes.Buffer
io.Copy(&buf, r)
w.Close()
os.Stdout = orig
if(buf.String() != "Some message") {
    t.Error("Failure!")
}

但每一项测试都有很多额外的工作。我希望有一个更标准的方法,或者一个抽象库来处理这个问题。

mxg2im7a

mxg2im7a1#

还有一件事要记住,没有什么能阻止你编写函数来避免样板文件。
例如,我有一个使用log的命令行应用程序,我写了这个函数:

func captureOutput(f func()) string {
    var buf bytes.Buffer
    log.SetOutput(&buf)
    f()
    log.SetOutput(os.Stderr)
    return buf.String()
}

然后像这样使用它:

output := captureOutput(func() {
    client.RemoveCertificate("www.example.com")
})
assert.Equal(t, "removed certificate www.example.com\n", output)

使用这个assert库:http://godoc.org/github.com/stretchr/testify/assert

von4xj4u

von4xj4u2#

你可以做三件事之一。第一个是使用示例。
该包还运行并验证示例代码。示例函数可以包括以“Output:”开始的结束行注解,并且当运行测试时与函数的标准输出进行比较。(比较忽略前导和尾随空格。)以下是示例的示例:

func ExampleHello() {
        fmt.Println("hello")
        // Output: hello
}

第二种(更合适的,IMO)是为IO使用假函数。在你的代码中,你可以:

var myPrint = fmt.Print

func (t *Thing) print(min_verbosity int, message string) {
    if t.verbosity >= minv {
        myPrint(message) // N.B.
    }
}

在你的测试中:

func init() {
    myPrint = fakePrint // fakePrint records everything it's supposed to print.
}

func Test...

第三种是将fmt.Fprintfio.Writer一起使用,在生产代码中是os.Stdout,但在测试中是bytes.Buffer

t1rydlwq

t1rydlwq3#

您可以考虑向函数添加return语句,以返回实际打印出来的字符串。

func (t *Thing) print(min_verbosity int, message string) string {
    if t.verbosity >= minv {
        fmt.Print(message)
        return message
    }
    return ""
}

现在,您的测试可以只检查返回的字符串与预期的字符串(而不是打印输出)。也许更符合测试驱动开发(TDD)。
而且,在您的产品代码中,不需要做任何更改,因为如果您不需要函数的返回值,您不必为其赋值。

aiqt4smr

aiqt4smr4#

将@Caleb的答案改编为testing和stdout,并从OP的代码中进行了一些修复:

func captureOutput(f func() error) (string, error) {
    orig := os.Stdout
    r, w, _ := os.Pipe()
    os.Stdout = w
    err := f()
    os.Stdout = orig
    w.Close()
    out, _ := io.ReadAll(r)
    return string(out), err
}

func TestMyFunc(t *testing.T) {
    output, err := captureOutput(func() error {
        err := myFunc("arg1", "arg2")
        return err
    })
    assert.Nil(t, err)
    assert.Equal(t, "foo", output)
}

注意Close()的位置。在我将其移动到ReadAll()之上之前,读取操作会挂起并导致测试错误,原因很明显(等待更多输入)。

相关问题