如何使用“测试”包在 Go 测试中打印?

Posted

技术标签:

【中文标题】如何使用“测试”包在 Go 测试中打印?【英文标题】:How do you print in a Go test using the "testing" package? 【发布时间】:2014-06-05 23:47:25 【问题描述】:

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

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

【问题讨论】:

这可能适用于 Go 1.14(2010 年第一季度)。见my answer below。 @VonC s/b Q1 2020 @user2133814 同意,确实应该是 2020 年,而不是 2010 年。answer below 确实提到了 2020 年。我已经编辑了上述答案,并参考了 Dave Cheney 关于该新功能的文章。 【参考方案1】:

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(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

正如 Dave Cheney 在“go test -v streaming output”中证明的那样,它确实在 Go 1.14 中:

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

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

优势,据 Dave 所说:

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

【讨论】:

照明,谢谢! :) 只是一件事,测试输出与此处的测试不匹配。 @bravmi 好点。你能用正确的更新输出编辑答案吗? 很高兴,一旦编辑队列释放了!让我借此机会非常感谢您的回答。 【参考方案2】:

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

【讨论】:

【参考方案3】:

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

【讨论】:

【参考方案4】:

例如,

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

Command go

Description of testing flags

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

Package testing

func (*T) Log

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

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

【讨论】:

verbose 是我要找的。​​span> anwa 查看你正在测试的方法中的日志输出 我不能在这里使用 fmt,因为 Visual Studio Code 删除了 fmt 的导入! 在Example_xxx()中似乎verbose不起作用。【参考方案5】:

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

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

fmt.X 打印语句 do 在测试中起作用,但是您会发现它们的输出可能不在您希望找到的屏幕上,因此,为什么应该使用 @ 中的日志记录方法987654341@.

如果您想查看没有失败的测试的日志,则必须提供go test -v 标志(v 表示详细程度)。更多关于测试标志的细节可以在这里找到:https://golang.org/cmd/go/#hdr-Testing_flags

【讨论】:

t.Log() 直到测试完成后才会显示,因此如果您尝试调试挂起或性能不佳的测试,您似乎需要使用 fmt。请参阅 PeterSO 的回答,了解在运行测试时使用 go test -v 显示 fmt.Println 的输出。 如果有人从 VSCode 运行测试,只需在您的 settings.json 中添加 "go.testFlags": ["-v"](来源:github.com/Microsoft/vscode-go/issues/1377)【参考方案6】:

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

【讨论】:

【参考方案7】:

有时我会进行测试

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

另外,您可以打印到:

fmt.Fprintln(os.Stderr, "hello)

【讨论】:

第一个可以是fmt.Println("hello") Visual Studio Code 在我尝试导入和使用时删除了 import fmt。 :( @micahhoover 这是预期的行为,是未使用导入,go 工具将删除它,因为它不会编译,添加 fmt.Fprintln(os.Stderr, "hello)首先

以上是关于如何使用“测试”包在 Go 测试中打印?的主要内容,如果未能解决你的问题,请参考以下文章

如何测试一个在Go中调用另一个API端点的服务?

如何使用Junit进行单元测试

Go 语言如何读取 excel 测试数据,简单易学

在 Jest 中测试失败时如何打印请求和响应?

如何将不同的错误接口实现存储在一起,然后在 Go 中使用它们进行类型比较?

[翻译]使用Spring Boot进行单元测试