Testing 如何测试函数';单元测试中的s输出(stdout/stderr)

Testing 如何测试函数';单元测试中的s输出(stdout/stderr),testing,go,stdout,Testing,Go,Stdout,我想测试一个简单的函数: func (t *Thing) print(min_verbosity int, message string) { if t.verbosity >= minv { fmt.Print(message) } } 但是如何测试函数实际发送到标准输出的内容呢?在Perl中实现我想要的功能。我知道我可以编写自己的所有样板文件,以便在Go中执行相同的操作(如上所述): 但是每一次测试都需要做很多额外的工作。我希望有一种更标准的方法,或者

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

func (t *Thing) print(min_verbosity int, message string) {
    if t.verbosity >= minv {
        fmt.Print(message)
    }
}
但是如何测试函数实际发送到标准输出的内容呢?在Perl中实现我想要的功能。我知道我可以编写自己的所有样板文件,以便在Go中执行相同的操作(如上所述):


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

您可以做两件事中的一件。首先是使用

该包还运行并验证示例代码。示例函数可能包括以“Output:”开头的结束行注释,并在运行测试时与函数的标准输出进行比较。(比较忽略前导和尾随空格。)以下是示例:

第二个(更合适的是,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.Fprintf
与生产代码中的
io.Writer
一起使用,即
os.Stdout
,但在测试中可能是
bytes.Buffer

还有一件事需要记住,没有什么可以阻止您编写函数来避免使用样板文件

例如,我有一个使用
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)

使用此断言库:.< /p> ,您可以考虑向函数中添加返回语句以返回实际打印出来的字符串。
func (t *Thing) print(min_verbosity int, message string) string {
    if t.verbosity >= minv {
        fmt.Print(message)
        return message
    }
    return ""
}
现在,您的测试可以根据预期的字符串(而不是打印输出)检查返回的字符串。也许更符合测试驱动开发(TDD)


而且,在您的生产代码中,不需要任何更改,因为如果您不需要函数的返回值,则不必指定它。

这可能是我将要做的,因为这是一个比@Ainar-G提供的更通用的解决方案,这要求我控制所有可能输出某些内容的代码。仅供参考,这不是例行程序安全的。当第一个go路由仍在记录时,第二个go例程可以将日志重置回
os.Stderr
。示例适用于具有确定性输出和fmt的情况。Fprint适用于可以控制打印代码的情况(如果打印代码在外部库或其他地方,则不能)。我想大多数时候,这些事情中有一件是真的。如果两者都不是真的,那么最好选择将写入os.Stdout的内容读入字符串。
output := captureOutput(func() {
    client.RemoveCertificate("www.example.com")
})
assert.Equal(t, "removed certificate www.example.com\n", output)
func (t *Thing) print(min_verbosity int, message string) string {
    if t.verbosity >= minv {
        fmt.Print(message)
        return message
    }
    return ""
}