go test -json

go test -json is go test with its output converted to a stream of JSON events — one object per line — by Go's built-in test2json. Every line says what happened (Action), in which package, to which test, and what the test printed. It is the format CI tools, IDEs, gotestsum and go-junit-report read instead of scraping --- FAIL: lines.

What a line looks like

{"Time":"2026-11-07T10:00:00.12Z","Action":"run","Package":"example.com/shop/cart","Test":"TestCoupon/SAVE10"}
{"Time":"2026-11-07T10:00:00.13Z","Action":"output","Package":"example.com/shop/cart","Test":"TestCoupon/SAVE10","Output":"    cart_test.go:42: total = 90.00, want 81.00\n"}
{"Time":"2026-11-07T10:00:00.14Z","Action":"fail","Package":"example.com/shop/cart","Test":"TestCoupon/SAVE10","Elapsed":0.02}
{"Time":"2026-11-07T10:00:00.41Z","Action":"fail","Package":"example.com/shop/cart","Elapsed":0.41}
Field Meaning
Action What happened — see the table below
Package The import path of the package under test
Test The test name; absent on package-level events
Elapsed Seconds, on pass, fail and skip
Output One chunk of printed output, newline included
FailedBuild Go 1.24+: on a package fail, the build that broke it

The actions

Action Meaning
start The package's test binary started (Go 1.20+)
run A test started
pause / cont A t.Parallel() test paused, then continued
output The test (or package) printed something
pass / fail / skip Terminal: the test or package finished with that result
bench A benchmark printed its result line
build-output Go 1.24+: compiler output, keyed by ImportPath
build-fail Go 1.24+: that build failed

A finished test gets exactly one terminal event. A package with no test files gets a single package-level skip.

The parts that bite

  • Parents fail with their subtests. TestCoupon/SAVE10 failing makes TestCoupon fail too, so a naive count of fail events double-counts. Subtests are named Parent/child, with spaces turned into underscores.
  • A panic can leave a test open. When the binary dies mid-test — a panic in a goroutine, -timeout reached, os.Exit — the package gets fail but the running test may get no terminal event at all. Its last output usually holds the panic: and stack. A timeout panic lists the tests that were still running.
  • A package can fail with every test passing. A panic in TestMain, a race detected after the tests, or a build failure all produce a package fail with no failing test. Reports that only list failing tests show a red build and an empty failure list.
  • Build errors weren't JSON before Go 1.24. Older toolchains print compiler errors to stderr as plain text. Without 2>&1 they vanish from the saved file; with it, the file mixes JSON and text lines, so parsers should skip lines that aren't JSON.
  • Parallel output interleaves. Events from t.Parallel() tests arrive mixed together. Group by Package + Test, never by position in the file. (=== NAME lines in Output mark the switch.)
  • Piping hides the exit code. go test -json ./... | tee test.json exits with tee's status, so a failing run looks green. Use set -o pipefail, or write to a file and check go test's status.
  • Cached results are replayed. A cached package re-emits its events with the original output. Use -count=1 when you need a fresh run.

Getting a readable report

Convert it to JUnit XML for CI test tabs:

gotestsum --junitfile junit.xml -- ./...
go test -json ./... 2>&1 | go-junit-report -parser gojson > junit.xml

Or skip the conversion: the free JUnit XML viewer reads go test -json output as-is. Packages become suites, tests and subtests become cases, a test with no terminal event shows as an error, a package that failed without a failing test (or failed to build) gets its own entry with the output, and a test that failed then passed on a re-run is marked flaky. Copy the Markdown summary into the pull request, or publish the report as a link.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Open the test report viewer →

Related