What does code coverage measure, and why is '100% coverage' not the same as 'well tested'?
Reading coverage as a diagnostic for untested code, not as a proxy for test quality.
Code coverage measures which parts of your code were EXECUTED while the tests ran. The common flavors: line coverage (which lines ran), statement coverage (similar), branch coverage (were both sides of each if/ternary/switch taken), and function coverage (which functions were called). Branch coverage is the most informative because it catches untested paths that line coverage hides — a line with `a && b` can be 100% line-covered while one branch never executed. Coverage is genuinely useful as a DIAGNOSTIC: it reliably tells you what is definitely NOT tested — uncovered lines are code no test touched, which is a real gap and a good place to look. What it does NOT tell you is whether the code is WELL tested, for a simple reason: coverage records that a line RAN, not that you ASSERTED anything meaningful about it. You can execute every line with zero assertions (or weak ones) and hit 100% while testing nothing — a test that calls a function and never checks its result 'covers' it. Coverage also can't see the cases you forgot: the empty array, the null, the boundary, the error path that your inputs never triggered; nor whether your assertions check the right thing. That's why chasing 100% is a trap — it drives people to write assertion-free tests for trivial getters just to hit a number, spending effort where risk is low and creating a false sense of safety. Use coverage the right way: as a floor and a spotlight (fail CI if it drops sharply, and review uncovered critical paths), not as a target that proves quality. Mutation testing is the tool that actually measures assertion strength — it changes your code and checks whether tests fail — but it's heavier. The honest summary: high coverage with weak assertions is worse than moderate coverage with sharp ones.
Interpreting a coverage report; pushing back on a blanket '100% coverage' mandate.
Coverage TELLS you: Coverage does NOT tell you:
- which lines/branches ran - whether you asserted anything useful
- what is definitely UNtested - whether you covered the edge cases
- whether the assertions are correct
# A "100% covered" test that proves nothing:
test('runs', () => { calculateTotal(cart); }); // no expect(...) at all!
# Prefer branch coverage; use it as a spotlight on gaps, not a target.
# Mutation testing measures assertion STRENGTH; coverage measures execution.