skip to content

Can a test suite with no assertions reach 100% line coverage, and what does that prove?

level: juniorimportance: must knowfreq 76%

answer

  1. Ask what the number actually records
  2. Execution is not verification
  3. The tool never sees an assertion
  4. Strong in one direction only
  5. Uncovered proves something, covered does not

basics

~20 s

Yes. Coverage instrumentation records which code the tests executed, not whether anything checked the result. A suite that calls every function and asserts nothing still reports 100%, and proves only that the code runs without crashing.

solid answer

~50 s

Coverage is an execution record, not a correctness record. A line is marked covered the moment a case runs it, and nothing in that measurement inspects an assertion, so a suite that walks every path of a fare calculator without ever comparing a returned fare to an expected one reports 100% while proving only the absence of crashes. The practical consequence is that coverage is trustworthy in one direction only: uncovered code is definitely untested, but covered code is at most *maybe* tested. Treat a low number as an alarm and a high number as the absence of one particular alarm rather than as evidence of quality. The questions a percentage cannot answer are what the cases assert, whether those assertions are specific enough to fail on a wrong value, and whether the risky boundaries were exercised at all.

code

pseudocode · 7 lines
pseudocode
test "peak surcharge path":
    fare = calculateFare(startZone = 2, endZone = 5, departAt = "09:29")
    log(fare)

test "off-peak path":
    fare = calculateFare(startZone = 2, endZone = 5, departAt = "11:05")
    log(fare)

go deeper

for a junior

Recall the one-sentence version: coverage records what ran, not what was checked. Be ready to say that a suite asserting nothing can still reach 100%, and that uncovered code is the part you can be sure was never tested.

for a middle

Explain the mechanism — counters attached to code units, incremented on execution, never inspecting the case — and give the weak-assertion spectrum from no assertion at all to a check that compares against a value recomputed by the same logic.

for a senior

Demonstrate the judgement of reading a real report: split it by module against risk, sample and read the cases behind a high figure, and ask whether a wrong value would actually turn one red rather than arguing about the percentage.

for a principal

Own the policy angle. Decide what the organization is allowed to do with this number, keep it off individual objectives, and be able to explain to a stakeholder who wants a single quality figure why executed-not-checked makes that figure unavailable.

### What a coverage percentage is actually made of A structural coverage percentage is produced by instrumentation. Before or during the run, a coverage tool attaches a counter to each unit of code it tracks, the suite executes, and the tool reports the proportion of counters that were touched at least once. Look carefully at what is being observed there: the **code**, during **execution**. Nothing in that pipeline ever inspects the test. The instrumentation cannot see an assertion, cannot see an expected value, and has no way of knowing whether the test that touched a line went on to check anything at all. That is why the answer to "can an assertion-free suite reach one hundred percent?" is a flat yes, and why it is not a trick question. Coverage answers "was this executed?". Correctness asks "was the result checked, and was the check right?". Those are different questions asked of different artefacts. ### A worked example Take a regional public-transport fare calculator: you pass a start zone, an end zone and a departure time, and it returns a fare. The suite has 3,214 cases and takes 27 minutes; the report says 91.4% of lines are covered, and the pricing package specifically reads 100%. Now suppose a case calls the calculator with zone 2, zone 5 and a departure of 09:29, and then does nothing with the returned fare except log it. Every line of the peak-surcharge branch is now green. Meanwhile the peak window is specified to begin at 09:30, and the comparison in the code was written with a greater-than where it needed a greater-than-or-equal. The traveller who departs at exactly 09:30 is charged the off-peak fare. Every line involved in that defect is covered. The percentage is blind to it because the defect lives in a **value**, and no one compared a value to anything. Raise the case slightly and nothing changes: assert that the fare is not null, or that no exception was thrown, and the number is identical while the off-by-one still ships. ### The asymmetry that makes coverage worth keeping The useful way to hold coverage in your head is as a one-directional signal: - **Uncovered means definitely untested.** That inference is sound. If the counter for the boundary branch was never touched, no case in the suite could possibly have checked it. - **Covered means at most maybe tested.** That inference carries no weight on its own. So coverage is a falsifier, not a verifier. It is genuinely good at finding whole regions nobody thought about — the error branch, the retry path, the module someone forgot to wire into the suite — and it costs almost nothing because it rides along with a run you were doing anyway. It is worthless as proof of quality. A candidate who says "coverage is useless" is overcorrecting; a candidate who says "we are at 91%, so we are in good shape" has missed the point entirely. ### The weak-assertion spectrum No assertion at all is the honest extreme, and it is rare in a real suite. The realistic middle ground is where suites actually live, and all of it leaves the percentage untouched: - asserting the result is not null, or merely that no exception escaped; - asserting that a collaborator was called, rather than that the outcome was right; - asserting on a field the change under test cannot affect; - comparing against a value computed by re-running the same logic the code uses, so the check can never disagree; - accepting a recorded output wholesale without anyone reading it. Each of these executes the same lines as a strong case and reports the same number. ### What a percentage structurally cannot express **Which inputs were used.** Coverage counts code units, not input classes. A single condition that compares the departure time against the peak cut-off is fully covered by one input, but the boundary needs at least two — one on each side. Off-by-one defects live inside covered lines by construction. **Whether the oracle is right.** The oracle is the thing that decides the observed behaviour is wrong. If the expected fare written into the case is itself wrong, the case is green, the line is covered, and the product misbehaves. **Whether the covered code matters.** A suite at 100% on trivial accessors and 40% on pricing can report the same total as the reverse, and the two suites are not remotely equivalent in value. **Sequences and state.** Coverage has no notion of order, of the second call after the first, or of state accumulated across a session. **Who did the executing.** A line touched by shared setup, by application bootstrap, or incidentally by an unrelated case counts exactly like a line a case deliberately exercised. ### What to ask instead For any change: were the changed lines executed **and** asserted; do the assertions name a concrete expected value; is each boundary exercised on both sides; and would the case actually fail if the returned value were wrong. That final question is the direct one, and deliberately introducing a fault to see whether anything goes red is the technique that answers it head-on.

  • If coverage cannot show that a case is any good, why measure it at all?
    Because the negative direction is sound and cheap. Uncovered code is definitely unexercised, so the report reliably finds whole regions nobody thought about — the error branch, the retry path, the module never wired into the suite. It rides along with a run you were doing anyway, so the cost is close to zero. The mistake is reading the positive direction as proof rather than as the absence of that one alarm.
  • A change arrives with 100% coverage on the lines it touched. What does a reviewer read next?
    The assertions. Look for a concrete expected value rather than a not-null or no-exception check, for each boundary exercised on both sides rather than once somewhere in the middle, and for checks that compare against an independently known value instead of against something recomputed by the same production logic. The reviewer's question is simply whether a wrong returned value would make this case fail.
  • Does a covered line guarantee that a test case executed it deliberately?
    No. The counter records execution, not intent or origin. A line touched by shared setup, by application startup, or incidentally on the way through an unrelated case is marked identically to one a case exercised on purpose. That is one reason a high figure can coexist with a module nobody has ever deliberately tested.

It is attendance, not comprehension: the register proves every student walked into the lecture hall, and says nothing about whether any of them understood a word.

saying these in an interview costs you the question

  • Says 100% coverage means the code is correct
  • Believes the coverage tool checks assertions
  • Reads a coverage report as a test-quality report
  • Cannot say what uncovered code proves
  • Dismisses coverage as entirely worthless
  • Counts a not-null check as a real assertion

context