Test Coverage Guide
Coverage Types
| Type | Measures | Weakness |
|---|---|---|
| Line / Statement | Which lines were executed | Misses unchecked return values, branch outcomes |
| Branch | Both true/false of every conditional | Better than line; still misses value combinations |
| Function | Which functions were called at all | Weakest; doesn't check behavior |
| Condition | Each boolean sub-expression | MC/DC required for safety-critical (avionics) |
| Mutation | Tests that catch code changes (bugs) | Slow to run; best indicator of test quality |
Coverage Tools by Language
# Go
go test ./... -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
go tool cover -func=coverage.out | grep total # print % total
# Enforce threshold in CI
go test ./... -coverprofile=cov.out && \
go tool cover -func=cov.out | grep total | awk '{print $3}' | \
awk -F'%' '{if ($1+0 < 80) exit 1}'
# JavaScript (Vitest / Jest)
npx vitest run --coverage
npx jest --coverage
# vitest.config.ts
export default {
test: {
coverage: {
provider: 'v8', // or 'istanbul'
reporter: ['text', 'json', 'html'],
thresholds: {
lines: 80,
branches: 75,
functions: 80,
statements: 80,
},
exclude: ['**/*.d.ts', '**/node_modules/**', 'src/generated/**'],
}
}
};
# Python (pytest-cov)
pytest --cov=src --cov-report=html --cov-fail-under=80
coverage run -m pytest && coverage report --fail-under=80
What Coverage Cannot Tell You
| 100% Coverage Doesn't Mean... | Example |
|---|---|
| Tests check the right thing | Test calls function but never asserts output |
| All edge cases are covered | Off-by-one errors in boundary conditions |
| Integrations work | Unit tests mock everything; real DB behaves differently |
| Tests prevent regressions | Tests that always pass even when code is broken |
| Code quality is good | 100% coverage on unmaintainable spaghetti code |