We deleted 40% of our tests. Coverage went up.
A test that has never failed is either protecting something or costing something. We spent three weeks finding out which, and deleted 2,140 of them.

Short answer
We deleted 2,140 of 5,300 tests — the ones that had never failed in 18 months, tested mocks rather than behaviour, or duplicated a higher-level test. Branch coverage rose from 71% to 74% because the remaining suite was rewritten to exercise real paths, and suite time fell from 22 minutes to 9.
On this page
Test suites grow the way attics do. Nobody decides to keep everything; nobody decides to throw anything out. Ours had 5,300 tests, took 22 minutes, and in the previous 18 months 61% of them had never failed once — not locally, not in CI, not ever.
How we found the tests to delete
We pulled 18 months of CI history — 14,600 runs — and joined it against the current test list. Then we scored each test on three questions.
- Has it ever failed? If not, what is it protecting?
- Does the assertion check behaviour, or check that a mock was called?
- Is there a higher-level test that would fail if this one's behaviour broke?
A test that scored "never failed, asserts on a mock, duplicated above" went on the list. That was 2,140 tests, or 40% of the suite.
Why does deleting tests raise coverage?
It does not, directly. Deleting a test can only lower or preserve coverage. What raised it was the second step: for every deleted cluster, we asked whether the behaviour was covered at the integration level, and where it was not, we wrote one integration test in its place. We wrote 212 of those. They exercised real code paths through real dependencies, and they touched branches the unit tests had been faking their way past.
Branch coverage went from 71% to 74%. Line coverage went from 83% to 85%. Neither number is the point, but both moved the direction people did not expect.
What a mock-only test looks like
it("saves the user", async () => {
const repo = { save: jest.fn() };
await createUser(repo, { email: "a@b.co" });
expect(repo.save).toHaveBeenCalledWith({ email: "a@b.co" });
});This test passes if createUser forwards its argument. It says nothing about whether a user can be created. We had 1,300 tests of this shape. They failed exactly once in 18 months, when someone renamed save to insert, and the failure taught nobody anything.
A test that only fails when you refactor is a tax on refactoring, not a guard against bugs.
The three weeks
Week one was the CI history join and the scoring — mostly a script and a spreadsheet. Week two was the deletions, in 14 pull requests grouped by module, each one paired with the replacement integration tests. Week three was the argument.
The argument matters. Every team has someone who wrote those tests, and "your tests never caught anything" is not a kind thing to hear. We framed it as a measurement, published the CI data internally, and let people challenge individual deletions. Forty-one tests were reinstated after challenge. Two of those have since failed on real regressions, which is two more than the other 2,099 managed.
What changed afterwards
- Suite time: 22 minutes to 9. Pull requests per day rose 18% over the following quarter.
- Flaky test rate: 3.1% of runs to 0.4%, because most of the flakes were in the deleted mock-heavy tests.
- Escaped defects per month: 6.3 before, 5.8 after. Not significant, but not worse.
We now run a quarterly report of tests that have not failed in 12 months. It is not a deletion list; it is a reading list. The Google testing blog's piece on test sizes is the framing we used for what to keep at each level.
The uncomfortable conclusion: most of our tests were there to make the coverage number feel safe, and the number was lying to us by three points in the wrong direction.
Frequently asked questions
- Is a test that never fails useless?
- Not necessarily. Some never fail because they guard code nobody touches. The question is whether it asserts on behaviour or on implementation — the latter tends to fail only during refactors.
- How long did the CI history analysis take?
- About four days for one engineer, most of it spent normalising test names across two renames of the test runner.
- Did any deleted test turn out to be needed?
- Two of the 41 reinstated after challenge have since caught real regressions. None of the remaining deletions has been missed in six months.
Sources
- Test Sizes — Google Testing Blog
- Mocks Aren't Stubs — martinfowler.com
Published by
Tecno Blocks
Engineering insights from Tecno Blocks covering web, mobile, AI, Web3, software architecture, product development, DevOps, and real-world case studies.
About the publication