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

问题描述 投票:16回答:3

我有一个要测试的简单功能:

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!")
}

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

testing go stdout
3个回答
26
投票

还要记住的一件事,没有什么能阻止您编写避免样板的功能。

例如,我有一个使用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("removed certificate www.example.com\n", output)

使用此断言库:http://godoc.org/github.com/stretchr/testify/assert


15
投票

您可以做两件事之一。首先是使用Examples

该程序包还将运行并验证示例代码。示例函数可能包括结尾的行注释,该注释行以“ 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.Fprintf与在生产代码中为io.Writeros.Stdout一起使用,但在测试中可以说为bytes.Buffer


1
投票

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

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

现在,您的测试可以只将返回的字符串与预期的字符串进行比较(而不是打印输出)。也许与测试驱动开发(TDD)更加兼容。

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

© www.soinside.com 2019 - 2024. All rights reserved.