How do you print in a Go test using the "testing" package?
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 usefmt
.
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 ofTestFoo
is done, then no output until all ofTestBar
is done, and again no more output until all ofTestBaz
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.
IfTestFoo
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
andt.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 streamt.Log
output as it happens, rather than hoarding it til the end of the test run.
Under Go 1.14 the
fmt.Println
andt.Log
lines are interleaved, rather than waiting for the test to complete, demonstrating that test output is streamed whengo 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.
Streamingt.Log
output will help Gophers debug those test failures without having to wait until the entire test times out to receive their output.
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.
For testing sometimes I do
fmt.Fprintln(os.Stdout, "hello")
Also, you can print to:
fmt.Fprintln(os.Stderr, "hello")
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