September 8, 2026
We inherited a project with 94% test coverage. Sounds like a dream, right? Then, in the first week, we shipped a bug that miscalculated VAT on orders above a certain amount. Everything green in CI, everything passing. Turns out there was a test that called the pricing function, got a result, and then checked nothing. Literally calculatePrice(order) with not a single expect. The coverage tool painted that line green because it executed. Nobody ever checked whether the result was correct.
That is the core problem with coverage as a goal. Coverage measures whether code ran, not whether anyone verified what it returned. Those are two completely different things, and we have been confusing them for years because one number is easy to put on a dashboard and the other requires thinking.
When I see a team chasing 100% coverage, I do not see discipline. I see a team writing tests to satisfy a number instead of catching mistakes. Because to reach that last twenty percent, you have to test getters, setters, trivial constructors, one-line object mappings, and branches that never happen in practice. Those tests catch nothing. They exist only to make the number look nice.
The worse part comes later. Every such test is a liability. When you change the implementation, thirty tests break, not because you broke anything, but because they are coupled to how something was written rather than what it should do. The team spends half a day patching tests instead of shipping. And then people start to hate tests, which is the worst possible outcome, because now tests are in their way instead of helping.
It is not about the number. A project with 60% coverage that covers all the business logic, edge cases, and the integrations around billing is healthier than a project with 95% that tests getters and skips the places where money actually leaks.
There is a simple question I ask before writing a test: if this breaks in production, how much does it hurt? If the answer is "nobody would even notice," there is nothing to test here. If the answer is "someone gets the wrong invoice" or "we lose an order," that is where you test to death.
Concretely, these are the places where a test almost always returns the investment:
And where not to spend time? On trivial code with no logic, on the framework (you do not test someone else's library), and on things better caught by an integration test than by twenty isolated unit tests with half a dozen mocks. When a test has more mocks than real code, that is a sign you are testing your imagination of the system, not the system itself.
This became urgent over the last year and a half. You tell a tool "write me tests for this module" and thirty seconds later you get a nice set that pushes coverage to 90%. It looks like a gift. The problem is that AI is excellent at writing tests that execute but that verify too little, or verify the wrong thing. You end up with a pile of expect(result).toBeDefined() instead of a check for whether the result is actually correct. The code ran, coverage is green, and the test would not catch it even if the function returned a completely wrong number.
This is where coverage as a measure ends and where the thing worth checking begins: mutation testing. The idea is simple and a little brilliant. The tool deliberately breaks your code, changes > to >=, inverts a condition, swaps + for -, and then runs your tests. If the tests still pass on the broken code, they verify nothing. A surviving mutation is proof that the test is theater.
The first time we ran mutation testing (Stryker on a JS project) against a module with 88% coverage, the mutation score was below 50%. Translated: half of our "checks" would not notice the code was broken. That was the moment the whole team stopped treating coverage as a measure of safety. You do not need mutation testing across the entire system, it is too slow and too expensive. But on critical business logic and on anything an AI wrote, it is the only honest way to check whether your tests are real or just green.
If you have a high-coverage project where bugs keep showing up anyway, do not add more tests. Do the opposite:
The goal was never to have a lot of tests. The goal is to sleep well because you know something will wake you if what matters breaks. That is not measured in percentages.
Related:
If you are not sure whether your tests catch what matters, or just fill a dashboard, we know how to tell the difference. Get in touch and we will look at where your testing is real protection and where it is just expensive theater.
