你可以使用”testing”包中的Print函数来在Go测试中打印输出。

huangapple go评论91阅读模式
英文:

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

问题

我正在使用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和手册中搜索过,但没有找到相关信息。

英文:

I'm running a test in Go with a statement to print something (i.e. for debugging of tests) but it's not printing anything.

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

When I run go test on this file, this is the output:

ok  	command-line-arguments	0.004s

The only way to really get it to print, as far as I know, is to print it via t.Error(), like so:

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

Which outputs this:

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

I've Googled and looked through the manual but didn't find anything.

答案1

得分: 271

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

英文:

The structs testing.T and testing.B both have a .Log and .Logf method that sound to be what you are looking for. .Log and .Logf are similar to fmt.Print and fmt.Printf respectively.

See more details here: http://golang.org/pkg/testing/#pkg-index

fmt.X print statements do work inside tests, but you will find their output is probably not on screen where you expect to find it and, hence, why you should use the logging methods in testing.

If, as in your case, you want to see the logs for tests that are not failing, you have to provide go test the -v flag (v for verbosity). More details on testing flags can be found here: https://golang.org/cmd/go/#hdr-Testing_flags

答案2

得分: 217

例如,

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命令
>
> 测试标志的描述
>
> -v
> 详细输出:记录所有运行的测试。即使测试成功,也会打印所有Log和Logf调用的文本。
>


> testing包
>
> func (*T) Log
>
> func (c *T) Log(args ...interface{})
>
> Log使用默认格式对其参数进行格式化,类似于Println,并将文本记录在错误日志中。对于测试,只有在测试失败或设置了-test.v标志时,才会打印该文本。对于基准测试,为了避免性能依赖于-test.v标志的值,该文本始终会被打印。

英文:

For example,

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 formats its arguments using default formatting, analogous to Println, and records the text in the error log. For tests, the text will be printed only if the test fails or the -test.v flag is set. For benchmarks, the text is always printed to avoid having performance depend on the value of the -test.v flag.

答案3

得分: 22

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

是的,在Go 1.13之前(包括Go 1.13)是这样的。

这个问题在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 -vTestFoo完成之前不会有任何日志输出,然后在TestBar完成之前也没有输出,直到TestBaz完成之前也没有输出。
如果测试正常工作,这是可以接受的,但如果存在某种错误,有几种情况下缓冲日志输出会有问题:

  • 当在本地进行迭代时,我希望能够进行更改,运行我的测试,立即查看日志中发生的情况以了解正在发生的情况,如果需要的话,按下CTRL+C提前关闭测试,进行另一个更改,重新运行测试,依此类推。
    如果TestFoo很慢(例如,它是一个集成测试),我在测试的最后才能看到日志输出。这会显著减慢迭代速度。
  • 如果TestFoo存在导致其挂起并永远无法完成的错误,我将无法获得任何日志输出。在这些情况下,t.Logt.Logf毫无用处。
    这使得调试非常困难。
  • 此外,不仅我没有日志输出,而且如果测试挂起时间过长,Go测试超时会在10分钟后终止测试,或者如果我增加了超时时间,许多CI服务器也会在一定时间后终止测试(例如,在CircleCI中为10分钟)。
    因此,现在我的测试被终止,而日志中没有任何信息告诉我发生了什么。

但是在可能的Go 1.14(2020年第一季度)中:CL 127120

testing: 在详细模式下流式传输日志输出

现在的输出是:

=== 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 streaming output”中所证实的那样:

在Go 1.14中,go test -v将会实时地流式传输t.Log输出,而不是将其保留到测试运行结束

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

根据Dave的说法,这对于那些在测试失败时经常重试很长时间的集成风格测试来说是一个很好的生活质量改进。流式传输的t.Log输出将帮助Gophers在整个测试超时之前调试这些测试失败,而无需等待输出。

英文:

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

Yes: that was the case up to Go 1.13 (August 2019) included.

And that was followed in golang.org issue 24929

> Consider the following (silly) automated tests:
>
> 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.

But for (possibly) Go 1.14 (Q1 2020): CL 127120

> ## testing: stream log output in verbose mode

The output now is:

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

It is indeed in Go 1.14, as Dave Cheney attests in "go test -v streaming output":

> In Go 1.14, go test -v will stream t.Log output as it happens, rather than hoarding it til the end of the test run.

> Under Go 1.14 the fmt.Println and t.Log lines are interleaved, rather than waiting for the test to complete, demonstrating that test output is streamed when go test -v is used.

Advantage, according to Dave:

> This is a great quality of life improvement for integration style tests that often retry for long periods when the test is failing.
Streaming t.Log output will help Gophers debug those test failures without having to wait until the entire test times out to receive their output.

答案4

得分: 11

有时候为了测试,我会这样写:

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

另外,你也可以打印到:

fmt.Fprintln(os.Stderr, "hello")
英文:

For testing sometimes I do

fmt.Fprintln(os.Stdout, &quot;hello&quot;)

Also, you can print to:

fmt.Fprintln(os.Stderr, &quot;hello&quot;)

答案5

得分: 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

希望这样能帮到你!

英文:

t.Log and t.Logf do print out in your test but can often be missed as it prints on the same line as your test. What I do is Log them in a way that makes them stand out, ie

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

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

	t.Logf(&quot;\n\nid: %v\n\n&quot;, *id)
})

which prints it to the terminal as,

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

答案6

得分: 3

警告:这里的答案不适用于同时测试多个包的情况。

@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/...将按预期流式输出。

英文:

WARNING: The answers here do not apply when testing multiple packages at once.

The answers from @VonC and @voidlogic are fantastic, but I wanted to bring the following thread to attention in case someone is running a permutation of go test -v ./...: https://github.com/golang/go/issues/46959

The issue lies in implementation nuances/difficulties related to running tests from multiple packages.

For example, running go test -v -count=1 -run TestOnlyOneInstanceOfThisTestExists ./multiple/packages/exist/below/... will only print logs after the test completes.

However, running go test -v -count=1 -run TestOnlyOneInstanceOfThisTestExists ./this/path/points/to/one/package/only/... will stream the output as expected.

答案7

得分: 0

如果你正在使用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
英文:

In case your using testing.M and associated setup/teardown; -v is valid here as well.

package g 

import (
	&quot;os&quot;
	&quot;fmt&quot;
	&quot;testing&quot;
)

func TestSomething(t *testing.T) {
	t.Skip(&quot;later&quot;)
}

func setup() {
	fmt.Println(&quot;setting up&quot;)
}

func teardown() {
	fmt.Println(&quot;tearing down&quot;)
}

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

答案8

得分: -2

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

// initZapLog用于初始化一个新的'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()
	// 使日志记录器在任何地方都可用
	zap.ReplaceGlobals(loggerMgr)
	defer loggerMgr.Sync() // 如果有的话,刷新缓冲区
	logger := loggerMgr.Sugar()
	logger.Debug("START")
	conf := initConf()
    /* 在这里进行你的测试
    if false {
        t.Fail()
    }*/
}
英文:

The *_test.go file is a Go source like the others, you can initialize a new logger every time if you need to dump complex data structure, here an example:

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

Then, every time, in every test:

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(&quot;START&quot;)
	conf := initConf()
    /* Your test here
    if false {
        t.Fail()
    }*/
}

huangapple
  • 本文由 发表于 2014年4月22日 04:27:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/23205419.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定