# What Is go test -json? — The test2json Event Stream, Line by Line

Canonical: https://commareports.com/glossary/go-test-json
Published: 2026-11-07

> What go test -json prints: one JSON event per line with Action, Package, Test, Elapsed and Output. The actions, how subtests, parallel tests, panics and build failures show up, and how to turn it into a readable report.

# 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

```json
{"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](/glossary/junit-xml) for CI test tabs:

```bash
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](/tools/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](/glossary/flaky-test). 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 →](/tools/junit-xml-viewer)**

### Related

- [JUnit XML](/glossary/junit-xml) — the format most CI test tabs read
- [Test reporter](/glossary/test-reporter) — console, JUnit and HTML outputs per runner
- [Go coverprofile](/glossary/go-coverprofile) — the other file `go test` writes
- [Share a Go test report](/share-go-test-report) · [Glossary](/glossary)
