All topics
Testingintermediate

Code Coverage and Its Limitations

What code coverage metrics actually measure (lines/branches executed by tests), and why high coverage numbers don't guarantee genuinely good tests.

Code coverage measures what proportion of a codebase's lines, branches, functions, or statements were actually executed at least once while running a test suite, typically reported as a percentage, and while it's a genuinely useful signal for spotting completely untested code, it's also one of the most commonly misunderstood and over-relied-upon metrics in testing, which is why interviewers like to probe whether you understand its real limitations rather than just how to generate a coverage report.

Code coverage is like tracking what percentage of a building's rooms someone physically walked through during a fire inspection — 100% room coverage tells you every room was visited at least once, but says absolutely nothing about whether the inspector actually checked each room's smoke detectors properly or just walked in, glanced around without looking at anything specific, and walked back out.

Key Concepts

1
Tools like Istanbul (which powers Jest's built-in --coverage flag) instrument code during test execution, tracking exactly which lines, branches (both sides of an if/ternary), functions, and statements were actually hit at least once by the running test suite, then report separate percentages for each of these different coverage dimensions — branch coverage specifically is often more revealing than simple line coverage, since a line containing an if/else can be 'covered' (executed at all) by a test that only ever exercises one of its two branches, silently leaving the other path completely unverified despite the line itself showing as 'covered.'
--coverageifif/else
2
The critical, often-repeated lesson is that 100% coverage only proves every line/branch *ran* at least once during the test suite — it says absolutely nothing about whether the assertions actually checked for correct behavior at all. A test that calls a function and asserts literally nothing about its result (or asserts something trivially true, like expect(true).toBe(true)) still contributes to that function's lines being marked 'covered,' despite verifying nothing meaningful whatsoever about whether the function actually behaves correctly — this gap between 'was executed' and 'was correctly verified' is exactly why coverage percentage alone is a genuinely poor, easily-gamed proxy for actual test quality.
expect(true).toBe(true)
3
A more nuanced, complementary technique for actually gauging test *quality* (rather than mere execution coverage) is mutation testing: deliberately introducing small, deliberate bugs ('mutants') into the source code — flipping a comparison operator, changing a boundary constant — and checking whether the existing test suite actually fails as a result; a mutant that survives (tests still pass despite the introduced bug) reveals a real gap in test quality that pure coverage percentage would never have surfaced, since the original, un-mutated line was very likely already marked 'covered' the whole time. In practice, most teams treat coverage as a useful floor for catching completely untested code (and for CI gates that fail a build if coverage drops unexpectedly), while remaining explicitly skeptical of chasing a specific high percentage as a goal in itself.