如何在Go测试中使用"testing"包吗?

我在Go中运行一个测试,用语句打印一些东西(即用于调试测试),但它没有打印任何东西。

func TestPrintSomething(t *testing.T) {
fmt.Println("Say hi")
}

当我在这个文件上运行go test时,输出如下:

ok      command-line-arguments  0.004s

据我所知,真正让它打印的唯一方法是通过t.Error()打印它,就像这样:

func TestPrintSomethingAgain(t *testing.T) {
t.Error("Say hi")
}

输出如下:

Say hi
--- FAIL: TestPrintSomethingAgain (0.00 seconds)
foo_test.go:35: Say hi
FAIL
FAIL    command-line-arguments  0.003s
gom:  exit status 1

我用谷歌搜索过手册,但什么也没找到。

215331 次浏览

结构体testing.Ttesting.B都有一个testing.B0和testing.B1方法,听起来就是你要找的。testing.B0和testing.B1分别类似于testing.B4和testing.B5。

在这里查看更多细节:http://golang.org/pkg/testing/#pkg-index

fmt.X打印语句在测试中工作,但你会发现它们的输出可能不在你期望找到它的屏幕上,因此,为什么你应该使用testing中的日志方法。

如果像你的例子一样,你想要查看没有失败的测试日志,你必须提供go test -v标志(v代表冗长)。关于测试标志的更多细节可以在这里找到:https://golang.org/cmd/go/#hdr-Testing_flags

例如,

package verbose


import (
"fmt"
"testing"
)


func TestPrintSomething(t *testing.T) {
fmt.Println("Say hi")
t.Log("Say bye")
}

go test -v
=== RUN TestPrintSomething
Say hi
--- PASS: TestPrintSomething (0.00 seconds)
v_test.go:10: Say bye
PASS
ok      so/v    0.002s

命令go . sh . sh

测试标志描述

-v
Verbose output: log all tests as they are run. Also print all
text from Log and Logf calls even if the test succeeds.

包测试 .

func (*T) Log .Log

func (c *T) Log(args ...interface{})

Log使用默认格式(类似于Println)格式化其参数,并在错误日志中记录文本。对于测试,只有在测试失败或-test时才会打印文本。设置V标志。对于基准测试,文本总是打印出来,以避免性能依赖于-test的值。v标志。

为了测试,我有时会这样做

fmt.Fprintln(os.Stdout, "hello")

此外,您还可以打印到:

fmt.Fprintln(os.Stderr, "hello")

*_test.go文件和其他文件一样是一个Go源文件,如果你需要转储复杂的数据结构,你可以每次初始化一个新的记录器,这里有一个例子:

// initZapLog is delegated to initialize a new 'log manager'
func initZapLog() *zap.Logger {
config := zap.NewDevelopmentConfig()
config.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
config.EncoderConfig.TimeKey = "timestamp"
config.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
logger, _ := config.Build()
return logger
}

然后,每次,在每次测试中:

func TestCreateDB(t *testing.T) {
loggerMgr := initZapLog()
// Make logger avaible everywhere
zap.ReplaceGlobals(loggerMgr)
defer loggerMgr.Sync() // flushes buffer, if any
logger := loggerMgr.Sugar()
logger.Debug("START")
conf := initConf()
/* Your test here
if false {
t.Fail()
}*/
}

t.Log()在测试完成之前不会显示,所以如果你试图调试一个挂起或执行糟糕的测试,似乎你需要使用fmt

是的:包括Go 1.13(2019年8月)之前都是这样。

并且在golang.org issue 24929中紧随其后

考虑以下(愚蠢的)自动化测试:

func TestFoo(t *testing.T) {
t.Parallel()


for i := 0; i < 15; i++ {
t.Logf("%d", i)
time.Sleep(3 * time.Second)
}
}


func TestBar(t *testing.T) {
t.Parallel()


for i := 0; i < 15; i++ {
t.Logf("%d", i)
time.Sleep(2 * time.Second)
}
}


func TestBaz(t *testing.T) {
t.Parallel()


for i := 0; i < 15; i++ {
t.Logf("%d", i)
time.Sleep(1 * time.Second)
}
}

如果我运行go test -v直到TestFoo全部完成,我才得到日志输出,那么没有输出,直到所有TestBar完成,同样没有更多的输出,直到所有TestBaz完成。
如果测试正在工作,这很好,但如果存在某种错误,则在少数情况下缓冲日志输出是有问题的:

    在本地迭代时,我希望能够进行更改,运行测试,立即查看日志中发生的情况以了解发生了什么,在必要时按CTRL+C提前关闭测试,进行另一项更改,重新运行测试,等等。
    如果TestFoo很慢(例如,这是一个集成测试),直到测试结束我才得到日志输出。 如果TestFoo有一个错误,导致它挂起并且永远不会完成,我将不会得到任何日志输出。在这种情况下,t.Logt.Logf根本没用。
    这使得调试非常困难 此外,我不仅没有日志输出,而且如果测试挂起太长时间,Go测试超时会在10分钟后终止测试,或者如果我增加超时时间,许多CI服务器也会在一定时间后(例如,CircleCI中的10分钟)没有日志输出时终止测试。
    所以现在我的测试被杀死了,我在日志中没有任何东西告诉我发生了什么

但是(可能)Go 1.14 (Q1 2020): CL 127120

测试:流日志输出在详细模式

现在的输出是:

=== RUN   TestFoo
=== PAUSE TestFoo
=== RUN   TestBar
=== PAUSE TestBar
=== RUN   TestBaz
=== PAUSE TestBaz
=== CONT  TestFoo
=== CONT  TestBaz
main_test.go:30: 0
=== CONT  TestFoo
main_test.go:12: 0
=== CONT  TestBar
main_test.go:21: 0
=== CONT  TestBaz
main_test.go:30: 1
main_test.go:30: 2
=== CONT  TestBar
main_test.go:21: 1
=== CONT  TestFoo
main_test.go:12: 1
=== CONT  TestBaz
main_test.go:30: 3
main_test.go:30: 4
=== CONT  TestBar
main_test.go:21: 2
=== CONT  TestBaz
main_test.go:30: 5
=== CONT  TestFoo
main_test.go:12: 2
=== CONT  TestBar
main_test.go:21: 3
=== CONT  TestBaz
main_test.go:30: 6
main_test.go:30: 7
=== CONT  TestBar
main_test.go:21: 4
=== CONT  TestBaz
main_test.go:30: 8
=== CONT  TestFoo
main_test.go:12: 3
=== CONT  TestBaz
main_test.go:30: 9
=== CONT  TestBar
main_test.go:21: 5
=== CONT  TestBaz
main_test.go:30: 10
main_test.go:30: 11
=== CONT  TestFoo
main_test.go:12: 4
=== CONT  TestBar
main_test.go:21: 6
=== CONT  TestBaz
main_test.go:30: 12
main_test.go:30: 13
=== CONT  TestBar
main_test.go:21: 7
=== CONT  TestBaz
main_test.go:30: 14
=== CONT  TestFoo
main_test.go:12: 5
--- PASS: TestBaz (15.01s)
=== CONT  TestBar
main_test.go:21: 8
=== CONT  TestFoo
main_test.go:12: 6
=== CONT  TestBar
main_test.go:21: 9
main_test.go:21: 10
=== CONT  TestFoo
main_test.go:12: 7
=== CONT  TestBar
main_test.go:21: 11
=== CONT  TestFoo
main_test.go:12: 8
=== CONT  TestBar
main_test.go:21: 12
main_test.go:21: 13
=== CONT  TestFoo
main_test.go:12: 9
=== CONT  TestBar
main_test.go:21: 14
=== CONT  TestFoo
main_test.go:12: 10
--- PASS: TestBar (30.01s)
=== CONT  TestFoo
main_test.go:12: 11
main_test.go:12: 12
main_test.go:12: 13
main_test.go:12: 14
--- PASS: TestFoo (45.02s)
PASS
ok      command-line-arguments  45.022s

它确实在Go 1.14中,正如Dave Cheney在“__abc1”中所证明的那样:

在Go 1.14中,go test -v将传输t.Log输出而不是把它储存到测试运行结束

在Go 1.14中,fmt.Printlnt.Log行是交叉,而不是等待测试完成,这表明当使用go test -v时,测试输出是流的。

根据戴夫的说法,优点是:

对于在测试失败时经常长时间重试的集成风格测试来说,这是一个很大的生活质量改进。
流式t.Log输出将帮助Gophers调试这些测试失败,而不必等到整个测试超时才能接收它们的输出

t.Logt.Logf确实会在测试中打印出来,但经常会被遗漏,因为它们打印在与测试相同的行上。我所做的是记录他们的方式,使他们脱颖而出,即

t.Run("FindIntercomUserAndReturnID should find an intercom user", func(t *testing.T) {


id, err := ic.FindIntercomUserAndReturnID("test3@test.com")
assert.Nil(t, err)
assert.NotNil(t, id)


t.Logf("\n\nid: %v\n\n", *id)
})

把它打印到终端,

=== RUN   TestIntercom
=== RUN   TestIntercom/FindIntercomUserAndReturnID_should_find_an_intercom_user
TestIntercom/FindIntercomUserAndReturnID_should_find_an_intercom_user: intercom_test.go:34:


id: 5ea8caed05a4862c0d712008


--- PASS: TestIntercom (1.45s)
--- PASS: TestIntercom/FindIntercomUserAndReturnID_should_find_an_intercom_user (1.45s)
PASS
ok      github.com/RuNpiXelruN/third-party-delete-service   1.470s

如果你使用testing.M和相关的设置/拆卸;-v在这里也是有效的。

package g


import (
"os"
"fmt"
"testing"
)


func TestSomething(t *testing.T) {
t.Skip("later")
}


func setup() {
fmt.Println("setting up")
}


func teardown() {
fmt.Println("tearing down")
}


func TestMain(m *testing.M) {
setup()
result := m.Run()
teardown()
os.Exit(result)
}
$ go test -v g_test.go
setting up
=== RUN   TestSomething
g_test.go:10: later
--- SKIP: TestSomething (0.00s)
PASS
tearing down
ok      command-line-arguments  0.002s