Test Craft & Technique

The 80% Test Coverage Trap

"You have to have at least 80% test coverage." Almost every engineer has heard it, and almost none can tell you where 80 came from. It's the most quoted number in testing and one of the least understood — because it measures the wrong thing well.

Code coverage measures which lines of your code a test suite executes — not whether those tests would actually catch a bug if one were introduced. A test can run a line, assert nothing meaningful about it, and still mark it "covered." So a coverage percentage tells you where your tests went, never whether they'd notice if the behaviour changed. That gap is the whole problem with treating 80% as a finish line.

To be fair — and this matters — coverage is not useless. Code that no test ever executes definitely isn't tested, so a very low number is a genuine red flag. The trap isn't measuring coverage. It's turning the measurement into the target, at which point people optimise the number instead of the safety it was supposed to stand for.

What does code coverage actually measure?

It measures execution, and nothing else. When your test suite runs, a coverage tool notes which lines, branches, or functions got touched, and reports the fraction. "82% line coverage" means 82% of your lines ran during the tests. It says precisely nothing about whether any test would fail if those lines started doing the wrong thing.

That distinction is easy to miss and expensive to ignore. Consider a test that calls a function, exercises every line inside it, and then asserts... nothing — or asserts only that it didn't throw. Every one of those lines counts as covered. Your dashboard turns green. And a defect could walk straight through untouched, because "the code ran" and "the code was checked" are different claims that coverage deliberately conflates.

Coverage tells you where your tests went. It never tells you whether they'd notice a bug when they got there.

Why is chasing an 80% coverage target counterproductive?

Because the moment a percentage becomes a goal, people hit the percentage — by the cheapest route available. And the cheapest route to coverage is almost never the route to good tests.

  • It rewards the wrong tests. Assertion-free tests that call code just to light up lines are the fastest way to move the number. They add coverage and zero protection.
  • It flattens risk. An 80% mandate implies every line deserves equal attention. It doesn't. The payment path and the tooltip-rendering helper are not equally worth your testing hours, but a blanket target pretends they are.
  • It manufactures busywork on trivia. Teams end up writing elaborate tests for getters, config, and generated code to drag a stubborn number up the last few points — effort that buys nothing.
  • It hides the real gap. A proud 90% suite can still have zero meaningful assertions on the one workflow that would sink the company. The number looks safe and isn't.

None of this means "don't measure coverage." It means don't manage to it. A team optimising for a coverage figure and a team optimising for catching bugs will write visibly different test suites — and only one of them sleeps well after a release.

What should you measure instead?

Shift the question from how much of the code ran to how much of the risk is checked. That's harder to put on a dashboard, which is exactly why the easy-but-wrong number wins by default.

  • Assertion quality over line count. Ask what would have to break for a test to fail. If the answer is "almost nothing," the test is decorative regardless of what it covers.
  • Coverage of consequences, not lines. Are the paths where a bug would actually hurt — money, data, auth, the thing your users can't tolerate being wrong — tested deliberately and hard? A system can survive gaps over trivia; it can't survive a gap over the checkout.
  • Mutation testing, if you want a real number. Mutation testing deliberately introduces small bugs and checks whether your tests catch them. It measures the thing coverage only pretends to — whether the suite would notice. It's slower, and it's honest.
  • The escape record. The bugs that reached production are your truest coverage report. They tell you where the tests weren't looking, in the only units that count.

The short version

  • Coverage measures which lines ran, not whether a bug would be caught.
  • Very low coverage is a real warning; a high number is not a safety guarantee.
  • The instant 80% becomes a target, people write tests that raise the number and protect nothing.
  • Measure assertion quality, deliberate coverage of the high-consequence paths, and — for a real signal — mutation testing.
  • Spend your testing hours where a bug would hurt, not where a dashboard is red.

Deciding where testing effort actually belongs — and defending that choice against a team that just wants the number green — is a judgment call, not a metric. Building that judgment is the core of my test management workshop: how to aim testing at real risk instead of at a percentage that only looks like safety.