Introduction: The Eternal Question "How Do I Output Something in a Test?"
\n\nYou are writing a test in Go. The logic is complex, the data is confusing, and you desperately need to see what is happening inside. You instinctively write fmt.Println("value =", x), run the test... and see silence. No output. Sound familiar?
This question is one of the most popular on Stack Overflow among beginner (and not only!) Go developers. It seems like what could be simpler than printing a string? But in the world of Go testing, there are its own rules and a gentleman's set of tools. In this article, we will break down all the ways to output in tests, organize the best practices of 2025, and show you how to avoid common pitfalls. Let's go!
\n\nWhy is fmt.Println Silent? (And When Does It Finally Speak Up)
\n\nLet's get straight to the point. When you run tests with the go test command (without flags), standard output (stdout) is suppressed. The Go test runner considers that if a test passes successfully, the user doesn't need to see tons of debug information. This is done for cleanliness and automation — imagine if every test printed 10 lines of log on every CI/CD run.
However, there are two exceptions when fmt.Println will still appear on the screen:
- \n
- The
-v(verbose) flag:go test -v. This mode enables detailed output for all tests. You will see messages fromfmt.Printlnand other functions. \n - Test failed: If a test completes with an error (triggered by
t.Errorort.Fatal), then thefmt.Printlnoutput from that test will be shown. This is very useful for debugging. \n
But relying on fmt.Println is bad practice. For tests in Go, there is a special, more correct, and powerful tool.
The Life of the Party: t.Log() and t.Logf()
\n\nThe testing.T object (which is passed to every test) has built-in methods for logging. Memorize them like the "Our Father": t.Log() and t.Logf().
package main\n\nimport (\n "testing"\n)\n\nfunc TestSum(t *testing.T) {\n result := 2 + 2\n // Simple logging\n t.Log("Starting the sum test")\n t.Log("Calculation result:", result)\n\n // Logging with formatting (like fmt.Printf)\n t.Logf("The sum of 2 and 2 is %d. Check passed!", result)\n\n if result != 4 {\n t.Errorf("Expected 4, got %d", result)\n }\n}\n\nRun this test with the -v flag:
go test -v -run TestSum\n\nAnd you will see beautiful, structured messages directly in the test output.
\n\nWhy is t.Log Better Than fmt.Println?
\n- \n
- Context: Messages from
t.Logare automatically tied to a specific test. In the-voutput, you can see which test printed what. \n - Controllability: The output of
t.Logis only shown with-vor when a test fails. This means your logs don't clutter the console during a normal run. \n - Safety: Unlike
fmt.Fprintf(os.Stderr, ...),t.Logworks correctly with parallel tests (t.Parallel) and does not cause race conditions. \n
The Dark Side of the Force: t.Error() and t.Fatal() as Output Tools
\n\nMany beginners use t.Error() only to stop a test on error. But in fact, it is an excellent (and correct!) way to output important diagnostic information, especially when a test fails.
The secret is that the message passed to t.Error() or t.Fatal() will always be shown, even without the -v flag. This makes them ideal for outputting critical