Share a Go test report

Go's testing story is deliberately terminal-shaped. go test ./... prints ok and moves on, which is correct for the person who ran it and useless for everyone else.

The two HTML artifacts Go can produce are both genuinely worth reading, and both die on the machine that made them.

The coverage page

This is the one worth sharing:

go test -coverprofile=cover.out ./...
go tool cover -html=cover.out -o coverage.html

coverage.html is a single self-contained file — annotated source, green for covered, red for not, with a per-file dropdown. That is a far better artifact than a coverage percentage, because it answers the actual question: which branch is untested, and does that matter?

The results page

go test has no built-in HTML reporter. The common routes:

# results as a page
go test -json ./... > test.json
go-test-report -f test.json -o report.html

# or JUnit XML for CI-native display
gotestsum --junitfile junit.xml ./...

Both are single files too. See share a JUnit report if your pipeline is already consuming the XML.

Publish it

go test -coverprofile=cover.out ./... || true
go tool cover -html=cover.out -o coverage.html

curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html coverage.html \
        --arg title "Coverage — $(git rev-parse --short HEAD)" \
        '{title: $title, html: $html}')"

One call, no assets — the single-file shape is what makes this easy. Use a scoped token (reports:write) from CI secrets and PATCH a saved report id, so one URL accumulates a revision per run instead of a new orphan link every build.

The || true matters: a failing test fails the step, and the publish has to survive it. In GitHub Actions add if: always(). See publishing from CI and GitHub Actions HTML reports.

Scripts run inside a sandboxed iframe (allow-scripts, no allow-same-origin), so the file selector and the source annotation work at the URL exactly as they do locally.

What the URL buys you

  • The PR links the coverage page, so "is this path tested?" is a click rather than a local run.
  • Review lands on the code. Select the uncovered branch, leave a thread — "this is the retry path, it needs a test" — and it stays anchored there across runs. See commenting on HTML.
  • Revision diffs beat the percentage. Two revisions, one diff, and you see which lines changed coverage rather than watching a number move by a third of a point.

Who can see it

Per report: private, your team, anyone signed in at your domain, or anyone with the link. The coverage page contains your source code, so team or private is the right default for a private repo (domain-gating is Enterprise). See sharing & access control.

Limits

  • Entry HTML: 5 MB. go tool cover -html inlines every covered source file, so a large monorepo can exceed that — generate per-module profiles and publish the modules people review. Assets: 25 MB per file, 250 MB and 500 files total.
  • Scripts run, sandboxed — no same-origin access.
  • 60 requests/minute per token.

Try it

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

Publish a coverage report →

Related