Vitest — Coverage¶
OWNER: marije, judith ALSO_USED_BY: antje LAST_VERIFIED: 2026-03-26 GE_STACK_VERSION: vitest ^4.0.18, @vitest/coverage-v8 ^4.0.18
Overview¶
Coverage measures how much source code is exercised by tests. GE uses V8 provider for speed and accuracy. Thresholds are enforced in CI. Agents use this page to understand coverage configuration, thresholds, and meaningful coverage strategies.
V8 vs Istanbul¶
| V8 | Istanbul | |
|---|---|---|
| Speed | Fast (native V8 instrumentation) | Slower (source code transformation) |
| Accuracy | Very accurate for Node/Vite | Battle-tested, well-understood |
| Setup | Zero config with @vitest/coverage-v8 |
Requires @vitest/coverage-istanbul |
| GE default | Yes | No |
CHECK: Coverage provider selection.
THEN: Use V8 (@vitest/coverage-v8) — it is the GE standard.
IF: V8 produces incorrect results for a specific edge case.
THEN: Switch that project to Istanbul and document why.
GE Coverage Thresholds¶
// vitest.config.ts
coverage: {
provider: "v8",
include: ["src/**/*.{ts,tsx}"],
exclude: [
"src/**/*.d.ts",
"src/**/index.ts", // Re-export barrels
"src/**/*.stories.{ts,tsx}", // Storybook stories
"src/types/**", // Type-only files
"src/**/constants.ts", // Static constants
],
thresholds: {
statements: 80,
branches: 75,
functions: 80,
lines: 80,
},
}
| Metric | Threshold | What it measures |
|---|---|---|
| Statements | 80% | Individual statements executed |
| Branches | 75% | if/else/switch paths taken |
| Functions | 80% | Functions called at least once |
| Lines | 80% | Source lines executed |
CHECK: Coverage thresholds are configured. THEN: Branches threshold is intentionally lower (75%) — some error paths are hard to reach. THEN: Never set any threshold below 70% — that signals missing tests. THEN: Never set thresholds at 100% — it incentivizes gaming over quality.
Meaningful Coverage (Not Gaming Metrics)¶
CHECK: Coverage is below threshold. IF: Tests exist but coverage is low. THEN: Identify untested branches and error paths — add targeted tests. IF: Coverage is at threshold but tests are shallow. THEN: Coverage is being gamed — fix the tests.
What Counts as Gaming¶
ANTI_PATTERN: Tests that call functions without asserting results.
// GAMING — this hits the line but proves nothing
it("runs the function", () => {
calculateTotal(100, 0.1) // no expect()
})
ANTI_PATTERN: Testing implementation details to inflate branch coverage.
// GAMING — tests internal branching, not behavior
it("enters the else branch", () => {
// Contrived input just to hit a branch
})
ANTI_PATTERN: Excluding files to meet thresholds. FIX: Only exclude files that genuinely cannot be unit tested (type files, re-export barrels, constants).
What Counts as Meaningful¶
// MEANINGFUL — tests behavior, verifies edge cases
describe("calculateShipping", () => {
it("returns free shipping for orders over 50 EUR", () => {
expect(calculateShipping(75)).toBe(0)
})
it("returns 4.95 for orders under 50 EUR", () => {
expect(calculateShipping(30)).toBe(4.95)
})
it("returns 4.95 for exactly 50 EUR (boundary)", () => {
expect(calculateShipping(50)).toBe(4.95)
})
it("throws for negative amounts", () => {
expect(() => calculateShipping(-10)).toThrow("Invalid amount")
})
})
Coverage in CI¶
# In GitHub Actions workflow
- name: Run tests with coverage
run: npx vitest run --coverage
- name: Check coverage thresholds
run: npx vitest run --coverage --coverage.thresholds.100
# Fails CI if any threshold is not met
CHECK: CI pipeline includes tests. THEN: Coverage runs on every PR. THEN: Coverage report is generated as an artifact. THEN: Threshold failure blocks merge.
Vitest v4 Coverage Changes¶
CHECK: Migrating coverage config from Vitest v3 to v4.
IF: Using coverage.all: true to include untested files in report.
THEN: Removed in v4 — define coverage.include patterns explicitly instead.
IF: Using line-level ignore comments (v8 ignore next).
THEN: Works in v4.1+ for both V8 and Istanbul providers.
// v4.1+: Ignore specific lines from coverage
/* v8 ignore start */
if (process.env.NODE_ENV === "development") {
enableDevTools()
}
/* v8 ignore stop */
Coverage for Changed Files Only¶
Vitest v4.1 introduces coverage.changed to limit coverage to modified files only.
Useful for large codebases where full coverage runs are slow.
CHECK: Coverage runs are slow in CI (> 2 minutes).
IF: The project has 500+ source files.
THEN: Consider enabling coverage.changed for PR checks.
THEN: Keep full coverage runs on main branch merges.
Viewing Coverage Reports¶
# Generate HTML report
npx vitest run --coverage --reporter=html
# Opens in browser
open coverage/index.html
CHECK: Coverage results need human review. THEN: Generate HTML report — it shows line-by-line highlighting. THEN: Focus review on red (uncovered) lines in business-critical files.
Cross-References¶
READ_ALSO: wiki/docs/stack/vitest/index.md READ_ALSO: wiki/docs/stack/vitest/patterns.md READ_ALSO: wiki/docs/stack/vitest/pitfalls.md READ_ALSO: wiki/docs/stack/vitest/checklist.md