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

How do you print in a Go test using the “testing” package?

我正在Go语言中运行一个带有打印内容的语句的测试(即用于测试的调试),但没有打印任何内容。

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

当我对此文件运行go test时,输出为:

1
ok      command-line-arguments  0.004s

据我所知,真正使其打印的唯一方法是通过t.Error()进行打印,如下所示:

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

输出以下内容:

1
2
3
4
5
6
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搜索并浏览了手册,但没有找到任何东西。


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

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

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

如果像您的情况一样,要查看没有失败的测试日志,请提供go test-v标志(v为冗长)。有关测试标志的更多详细信息,可以在以下位置找到:http://golang.org/cmd/go/#hdr-Description_of_testing_flags


例如,

1
2
3
4
5
6
7
8
9
10
11
package verbose

import (
   "fmt"
   "testing"
)

func TestPrintSomething(t *testing.T) {
    fmt.Println("Say hi")
    t.Log("Say bye")
}
1
2
3
4
5
6
7
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

1
2
3
-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

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

Log formats its arguments using default formatting, analogous to
Println, and records the text in the error log. The text will be
printed only if the test fails or the -test.v flag is set.


t.Log() will not show up until after the test is complete, so if you're trying to debug a test that is hanging or performing badly it seems you need to use fmt.

是的:包括Go 1.13(2019年8月)就是这种情况。

之后在golang.org问题24929中

Consider the following (silly) automated tests:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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)
    }
}

If I run go test -v, I get no log output until all of TestFoo is done, then no output until all of TestBar is done, and again no more output until all of TestBaz is done.
This is fine if the tests are working, but if there is some sort of bug, there are a few cases where buffering log output is problematic:

  • When iterating locally, I want to be able to make a change, run my tests, see what's happening in the logs immediately to understand what's going on, hit CTRL+C to shut the test down early if necessary, make another change, re-run the tests, and so on.
    If TestFoo is slow (e.g., it's an integration test), I get no log output until the very end of the test. This significantly slows down iteration.
  • If TestFoo has a bug that causes it to hang and never complete, I'd get no log output whatsoever. In these cases, t.Log and t.Logf are of no use at all.
    This makes debugging very difficult.
  • Moreover, not only do I get no log output, but if the test hangs too long, either the Go test timeout kills the test after 10 minutes, or if I increase that timeout, many CI servers will also kill off tests if there is no log output after a certain amount of time (e.g., 10 minutes in CircleCI).
    So now my tests are killed and I have nothing in the logs to tell me what happened.

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

testing: stream log output in verbose mode

现在的输出是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
=== 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

为了测试有时我会

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

另外,您可以打印到:

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


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

1
2
3
4
5
6
7
8
9
// 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
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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()
    }*/
}