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



我正在 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

我用谷歌搜索并浏览了手册,但没有找到任何东西。

结构testing.Ttesting.B都有一个.Log.Logf的方法,听起来就是你要找的。 .Log.Logf分别类似于fmt.Printfmt.Printf

在此处查看更多详细信息:http://golang.org/pkg/testing/#pkg-index

fmt.X print 语句确实可以在测试中工作,但您会发现它们的输出可能不在您希望找到它的屏幕上,因此,为什么您应该在 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

命令去

测试标志说明

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

封装测试

函数 (*T( 日志

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

日志使用默认格式(类似于 Println(设置其参数的格式,并将文本记录在错误日志中。对于测试,仅当测试失败或设置 -test.v 标志时,才会打印文本。对于基准测试,始终打印文本以避免性能依赖于 -test.v 标志的值。

t.Log()直到测试完成后才会显示,因此,如果您尝试调试挂起或性能不佳的测试,则似乎需要使用fmt

是的:包括Go 1.13(2019年8月(之前的情况。

随后

golang.org期 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(2020 年第 1 季度(: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 在"go test -v流输出"中证明的那样:

在 Go 1.14 中,go test -v 将在输出发生时流式传输t.Log而不是将其囤积到测试运行结束

在 Go 1.14 下,fmt.Println 行和t.Log行是交错的,而不是等待测试完成,这表明在使用 go test -v 时测试输出是流式传输的。

优势,根据戴夫的说法:

对于集成样式测试来说,这是一个

很好的生活质量改进,当测试失败时,集成样式测试通常会重试很长时间。
流式t.Log输出将帮助Gophers调试这些测试失败,而不必等到整个测试超时才能接收其输出。

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

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

此外,您还可以打印到:

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

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("nnid: %vnn", *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
<</div> div class="one_answers">

警告:此处的答案不适用于一次测试多个包的情况。

@VonC和@voidlogic的答案非常棒,但我想引起以下线程的注意,以防有人运行go test -v ./...排列:https://github.com/golang/go/issues/46959

问题在于与从多个包运行测试相关的实现细微差别/困难。

例如,运行go test -v -count=1 -run TestOnlyOneInstanceOfThisTestExists ./multiple/packages/exist/below/...只会在测试完成后打印日志。

但是,运行go test -v -count=1 -run TestOnlyOneInstanceOfThisTestExists ./this/path/points/to/one/package/only/...将按预期流式传输输出。

如果您使用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

*_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()
    }*/
}

最新更新