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.