Read the Assertions, Not the Coverage

Eight green tests on a discount-approval resolver asserted every failure path and none of the intended behaviour. A @TestVisible bypass had also routed the production query out of the suite.

Eight tests. All green. On the resolver that decides whether a discount needs a manager's approval.

Not one of them asserted that a discount ever reaches a manager.

What the Number Actually Counts

Coverage counts lines executed. It does not count behaviour verified.

Those two things overlap often enough that most of us stopped treating them as separate. A line that runs during a test feels tested. It ran, the test passed, the percentage went up.

But a line runs whether or not anything downstream of it is checked. You can execute the entire class, assert nothing that matters, and report a number that says the feature is well covered. The suite is not claiming the behaviour is correct. It is claiming the code did not throw.

Those are very different claims, and only one of them is printed on the dashboard.

Everything Except the Point

Here is what the eight tests actually asserted.

The error path. The no-match path. Both automatic fallbacks. Four failure and default outcomes, each properly checked.

Here is what no test asserted: the manager-approval path, the matched-rule path, and the at-ceiling boundary.

The feature exists to route discounts to a manager when they exceed what an agent may grant. That is the whole reason the resolver was written. It was the one outcome nobody had written an assertion for.

This is not a rare shape. Failure paths are easy to assert because they produce a single, obvious, stable value — an error code, a null, a fallback constant. The intended behaviour is harder: it needs real data, a matching rule, a record in the right state. So the easy assertions get written first, the suite goes green, coverage clears the gate, and the hard ones never get written because the gate already cleared.

The Two Tests That Tested Nothing

Two of the eight set a value and read it back.

Construct the object, assign a field, assert the field holds what was just assigned. That test cannot fail while the language works. It asserts the assignment operator.

It also runs lines, so it counts. A suite can contain tests that are pure arithmetic on the platform's own behaviour, and nothing in the tooling will distinguish them from a test that verifies a business rule. To the coverage report they are identical.

That is the part worth sitting with: two of eight tests — a quarter of the suite — were structurally incapable of reporting a defect.

The Flag That Removed Production From the Suite

Then there was the bypass.

A @TestVisible flag swapped the production lookup — WHERE Name = :referenceNumber — for a test-only shortcut. Flip the flag in setup, and the class finds its record by a different route entirely.

The flag exists for a real reason. A freshly created record has no reference number yet, so the production query has nothing to match on and the test data cannot be built the way a live record arrives. Somebody hit that wall and solved it the pragmatic way.

The consequence is that the query which actually runs for a live customer was exercised by no test at all. Not weakly tested. Not tested.

Every filter, every field reference, every assumption about uniqueness in that WHERE clause sat outside the tested surface — while the coverage report counted the class as covered, because the lines around the bypass all ran.

What It Cost

An at-ceiling discount routed to a manager when it should have auto-approved, and it reached QA.

The boundary was never asserted, so nothing between the developer's keyboard and a tester's session was capable of noticing. The suite was green the whole way.

How to Read an Inherited Suite

When you pick up a feature with a passing test class, twenty minutes of reading beats any percentage. Four things to look for.

  • Find the happy path the feature exists for, and check it is asserted. Name the outcome the business asked for in one sentence, then search the test class for an assertion on that outcome. If it is not there, nothing else in the suite matters.
  • Check both sides of every boundary, and the boundary itself. A threshold with assertions comfortably above and comfortably below is the same gap expressed in Apex as an untested limit expressed in a manual test case.
  • Search the class for @TestVisible and any bypass flag. For each one, ask which production code path it switches off and whether anything else exercises that path. A bypass with a legitimate reason still removes code from the tested surface.
  • Count assertions, not tests. A test with no assertion, or with an assertion on a value it set itself, is a line-executor. Knowing how many of those you have changes how you read the number.

Why This Survives Review

Because a code review looks at the diff, and the diff is green.

Nobody in a pull request is asked "which behaviours does this suite verify?" They are asked whether coverage clears the threshold, and it does. The reviewer sees tests present, a percentage in range, and a build passing. Every visible signal says done.

Reading assertions is slower, unglamorous, and produces no artefact. It is also the only step in the process where anyone finds out whether the tests are about the feature.

And it is not the developer's failure. A suite written by the person who wrote the logic asserts the logic as they understand it — which is precisely the understanding that produced the gap. Someone who does not share that model has to read it, and on most teams that someone is you.

Open the last test class you trusted. How many of its assertions are about the behaviour the feature was built for?