Somewhere along the way, "test coverage percentage" became a proxy for "code quality" in a lot of engineering orgs — a number on a dashboard that goes green above 80% and red below it, treated as a meaningful signal of how safe the codebase is to change. It's a genuinely useful metric for one narrow thing, and a surprisingly poor one for the thing most teams actually use it to measure.
What Coverage Actually Tells You (And What It Doesn't)
Test coverage measures which lines of code executed during a test run. That's it. It says nothing about whether the test actually verified the correct behavior of that code, or just happened to run through it on the way to an assertion about something else entirely.
_javascript
function calculateDiscount(price, percent) {
return price - (price * percent / 100);
}
test('calculateDiscount runs without crashing', () => {
calculateDiscount(100, 10); // executes every line
expect(true).toBe(true); // asserts nothing meaningful
});_
This test gives you 100% coverage on that function. It also verifies literally nothing about whether the discount calculation is correct. A codebase can hit an impressive coverage number while its tests assert almost nothing useful — and coverage tooling has no way to distinguish that from genuinely rigorous testing, because it isn't designed to.
Coverage Rewards Breadth, Not the Right Depth
A team chasing a coverage target under deadline pressure will naturally gravitate toward whatever raises the number fastest — usually simple, low-risk functions that are easy to execute in a test. Meanwhile, the genuinely complex, bug-prone logic — the part with five interacting conditionals and three edge cases — often gets one test for the happy path and nothing for the branches that actually matter, because writing those tests takes real thought and the coverage tool doesn't distinguish "tested thoroughly" from "tested once."
The result is a codebase where the coverage number looks reassuring while the actual risk sits almost entirely in the untested branches of the complex logic — exactly where bugs are most expensive when they surface in production.
Mutation Testing Exposes What Coverage Hides
If you want to know whether your tests actually verify correct behavior, mutation testing is a far sharper signal than line coverage. It works by deliberately introducing small bugs into your code — flipping a comparison operator, changing a boundary condition — and checking whether your test suite catches the change.
_javascript
// Original: if (age >= 18)
// Mutated: if (age > 18)
// If your tests still pass, they weren't actually testing the boundary _
condition
A codebase can have 95% line coverage and a mutation score under 40%, meaning most of those "covered" lines have no test that would actually catch a real bug introduced there. That gap between line coverage and mutation score is often the clearest evidence that a coverage target has been optimized rather than genuinely earned.
What to Track Alongside Coverage, Not Instead of It
Coverage isn't worthless — a function with zero coverage is a legitimate risk signal. It's just insufficient on its own. A more honest picture combines it with:
Mutation score, at least on your most critical or complex modules, to verify tests actually assert meaningful behavior
Escaped defect rate — bugs found in production that should have been caught by tests — tracked over time as the real feedback loop on test quality
Review of test assertions themselves during code review, not just whether a test exists, checking that the assertions actually verify the behavior that matters
Coverage of edge cases and error paths specifically, not just aggregate percentage across the whole codebase
Where This Matters Most in AI-Assisted Development
This distinction gets sharper as more code — and more tests — get an initial draft from an AI assistant. A model asked to "add tests for this function" will readily produce tests that execute every line and raise the coverage number, without necessarily probing the edge cases a senior engineer would think to check specifically because they've been burned by that category of bug before. Teams that pair rigorous human review with AI-assisted development tend to treat AI-generated tests as a first draft to be scrutinized for actual assertion quality, not a finished deliverable just because the coverage report went green.
The Takeaway
A high coverage number is necessary but nowhere near sufficient for confidence in a codebase. It tells you code was executed during a test run — not that anything meaningful was verified. Teams that want tests they can actually trust need to look past the aggregate percentage and ask a harder question: if a real bug were introduced right here, would any of these tests actually catch it?
Anchor text used above: "Teams that pair rigorous human review with AI-assisted development" → links to https://www.zoraz.net/
Top comments (0)