Code Coverage in Go

Measure which lines your Go tests run with -cover and -coverprofile, view an HTML coverage report, and use coverage numbers sensibly.

Coverage tells you which lines of your code ran during tests. It does not tell you whether the tests checked the right things, but it is very good at showing code that no test touches at all.

Getting a Coverage Percentage

go test -cover ./...
ok  	example.com/shop/internal/price	0.004s	coverage: 87.5% of statements
ok  	example.com/shop/internal/order	0.006s	coverage: 61.2% of statements

The percentage is the share of statements that ran at least once.

Seeing Exactly Which Lines Ran

Write a coverage profile, then open it as HTML:

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

The second command opens a browser page with your source code. Lines that ran are green, lines that did not are red. This view is much more useful than the percentage: it shows you, for example, that every error branch in a function is red because no test ever makes the dependency fail.

For a quick text summary per function:

go tool cover -func=cover.out
example.com/shop/internal/price/price.go:5:	WithTax		100.0%
example.com/shop/internal/price/price.go:9:	Discount	50.0%
total:						(statements)	81.8%

Coverage Across Packages

By default, a package's coverage only counts lines in that same package. If tests in order call into price, those price lines are not counted. To count them:

go test -coverpkg=./... -coverprofile=cover.out ./...

Coverage of a Running Program

Since Go 1.20 you can also measure coverage for a whole program, not just tests, which is useful for integration tests:

go build -cover -o app ./cmd/app
GOCOVERDIR=covdata ./app   # run your scenario
go tool covdata percent -i=covdata

High coverage is not the goal:

A test that calls a function without checking its result still counts every line as covered. Chasing 100% leads to tests that run code without checking anything. Use coverage to find untested code, especially red error branches, then write tests that check real behavior.

Which code matters most:

Business rules, parsers and error handling are where missing tests hurt. Generated code, simple getters and main wiring are usually fine to leave lower.

Profiles in version control:

cover.out and covdata/ are build output. Add them to .gitignore instead of committing them.

Using coverage well:

You look at the HTML report after writing a feature, add tests for red branches that matter, and keep an eye on the trend in CI rather than enforcing an exact number.

Summary

go test -cover prints a coverage percentage, and -coverprofile=cover.out with go tool cover -html=cover.out shows exactly which lines ran. Use -coverpkg=./... to count lines across packages and go build -cover for whole-program coverage. Treat coverage as a map of untested code, not a score to maximize.