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


129

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

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


Go 1.14(2010年第一季度)可能会这样做。请参阅下面的答案
VonC

@VonC s / b 2020
user2133814

@ user2133814同意,应该确实是2020年,而不是2010年。下面答案确实提到了2020年。我编辑了上述答案,并参考了Dave Cheney关于该新功能的文章。
VonC

Answers:


142

结构testing.Ttesting.B都具有.Log.Logf方法,这听起来似乎是您想要的。.Log.Logf分别类似于fmt.Printfmt.Printf

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

fmt.X打印语句确实可以在测试中使用,但是您可能会发现它们的输出可能不在您期望找到它的屏幕上,因此,为什么要使用中的日志记录方法testing

如果像你的情况,你想看到对于未失败的测试日志,你必须提供go test-v标志(v对于冗长)。有关测试标志的更多详细信息,请参见:https : //golang.org/cmd/go/#hdr-Testing_flags


15
t.Log()直到测试完成后才会显示,因此,如果您要调试挂起或性能不佳的测试,似乎需要使用fmt。有关在运行测试时使用go test -v显示fmt.Println的输出,请参见PeterSO的答案。
voutasaurus

142

例如,

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.

包装测试

func(* T)日志

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

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


21
verbose是我一直在寻找的东西。
cevaris 2014年

2
anwa在方法中查看日志输出ou正在测试自己
filthy_wizard

7

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(Q1 2020):CL 127120

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

现在的输出是:

=== RUN   TestFoo
=== PAUSE TestFoo
=== RUN   TestBar
=== PAUSE TestBar
=== RUN   TestGaz
=== PAUSE TestGaz
=== CONT  TestFoo
    TestFoo: main_test.go:14: hello from foo
=== CONT  TestGaz
=== CONT  TestBar
    TestGaz: main_test.go:38: hello from gaz
    TestBar: main_test.go:26: hello from bar
    TestFoo: main_test.go:14: hello from foo
    TestBar: main_test.go:26: hello from bar
    TestGaz: main_test.go:38: hello from gaz
    TestFoo: main_test.go:14: hello from foo
    TestGaz: main_test.go:38: hello from gaz
    TestBar: main_test.go:26: hello from bar
    TestFoo: main_test.go:14: hello from foo
    TestGaz: main_test.go:38: hello from gaz
    TestBar: main_test.go:26: hello from bar
    TestGaz: main_test.go:38: hello from gaz
    TestFoo: main_test.go:14: hello from foo
    TestBar: main_test.go:26: hello from bar
--- PASS: TestFoo (1.00s)
--- PASS: TestGaz (1.00s)
--- PASS: TestBar (1.00s)
PASS
ok      dummy/streaming-test    1.022s

正如Dave Cheney在“ go test -v流输出 ”中所证明的,它确实在Go 1.14中:

在Go 1.14中,go test -v在发生流时t.Log输出流,而不是在测试运行结束之前将其存储起来

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

根据Dave的优势:

对于集成样式测试,这可以提高生活质量,当测试失败时,集成样式测试通常会长时间重试。
流式t.Log输出将帮助Gophers调试那些测试失败,而不必等到整个测试超时才能接收它们的输出。



2

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

-2

*_test.go文件是Go源文件,与其他文件一样,如果您需要转储复杂的数据结构,则每次都可以初始化一个新的logger,这里是一个示例:

// 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()
    }*/
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.