QA · Read 9 min

Line, branch and condition coverage: they are not all worth the same

Covering lines is easy and deceptive. Covering branches and conditions is where the real bugs hide. The technical difference that changes how much you trust your number.

No, they are not all worth the same. Line coverage tells you how many statements ran; branch coverage, how many decisions took both paths; condition coverage, how many boolean subexpressions were tested both true and false. They are three different rulers that often land on the same percentage while measuring very different things, and mixing them up is why a "90 % coverage" number can reassure a team while the bugs keep slipping through. This article pulls the three apart, shows with code why the one that is easiest to raise is the one that protects you least, and explains which to watch when you actually want to trust your number.

The word "coverage" sounds like a single thing, and that is where the misunderstanding starts. It is a family of white box metrics: each one measures how much of the code's internal structure your tests touched, but each defines "touched" more strictly than the last. The ISO/IEC/IEEE 29119-4 standard catalogs them as structural coverage criteria, and the ISTQB syllabus treats them as the gauge of how deep your suite actually went [1][2]. The metric is not the problem: the problem is believing that a high number on the cheapest ruler equals well tested software.

01 · the easy rulerLine coverage: it counts statements, not logic

Line coverage (or statement coverage) answers a simple question: of all the executable lines in the code, how many did at least one test run? If you have one hundred lines and your tests executed ninety, you have 90 % line coverage. It is the metric almost every tool shows by default, and also the easiest one to inflate.

The flaw is fundamental, not a matter of calibration. A "covered" line only means the interpreter stepped over it, not that anyone checked it does the right thing, nor that both of its possible outcomes were tested. A condition with no else block can show 100 % line coverage without ever testing what happens when that condition is false, because there is no line to execute in the false case. The line existed, it ran once through the true side, and the ruler was satisfied.

Covering a line proves the code passed through there. It does not prove it passed through correctly, nor that you tested the path it did not take.

Figura 1 · una prueba, 100 % de líneas, media rama sin tocar
aplicarDescuento(monto, esVip) precio = monto si esVip: ← decisión (sin bloque "si no") precio = monto * 0.9 devolver precio Prueba única: esVip = verdadero Líneas ejecutadas: 4 de 4 → 100 % de líneas. Rama "esVip = falso": nunca recorrida → 50 % de ramas.
The very case that maxes out line coverage leaves half the decision untouched. If a non VIP customer triggers an error, this suite never catches it and the number still reads "100 %".

02 · the honest rulerBranch coverage: every decision, on both of its exits

Branch coverage (or decision coverage) raises the bar. It no longer counts lines: it counts the exits from each decision point. An if has two branches, true and false; a loop has the branch that enters and the one that skips; a switch has one per case. To reach 100 % branch coverage, your tests have to make every decision take both paths at least once.

That shift in focus is exactly what Figure 1 was missing. The same function with the same single test shows 100 % lines and only 50 % branches, because the "not VIP" branch never ran. Branch coverage forces you to add the second case, and with it comes the scenario where the errors really hide: the path the programmer assumed was trivial and never tested. That is why the ISTQB notes that decision coverage is stronger than statement coverage, and that 100 % of the former guarantees 100 % of the latter, but not the other way around [2].

A hierarchy, not a list

These criteria are nested: covering every branch implies covering every line, but not the reverse. That is why comparing "90 % lines" against "90 % branches" between two projects is misleading: the second 90 % cost more and protects more. When someone hands you a coverage percentage, the first question is not how much, but of what.

Even so, branch coverage has a blind spot of its own, and it is subtle. It treats each decision as a closed box: it is content once the whole condition comes out true one time and false another. It does not look inside that condition. And when the condition combines several checks with and / or, that is where the honest ruler still falls short.

03 · the fine rulerCondition coverage: inside the boolean

Consider a compound decision: if (age >= 18 and hasPermit). Branch coverage is satisfied with two cases that make the whole expression true and false. You can pull that off by testing only (20, true) and (15, false). In both cases the variable hasPermit moved together with the age: you never checked what happens with an adult without a permit, nor with a minor with one. The branch was "covered" and half the internal logic went untouched.

Condition coverage attacks exactly that: it requires each boolean subexpression to be evaluated true and false separately. Its rigorous variant, MC/DC (Modified Condition/Decision Coverage), goes further: it asks you to prove that each condition, on its own, can change the outcome of the whole decision. This is no academic whim. Critical avionics demands it by regulation: the DO-178C standard mandates MC/DC for level A software, the kind whose failure can cost lives [3]. When a bug is paid for not with a retry but with an accident, the fine ruler stops being optional.

Figura 2 · la misma decisión, tres exigencias distintas
Decisión: edad >= 18 Y tienePermiso LÍNEAS RAMAS CONDICIONES basta 1 paso V y F del total cada parte, V y F (20, sí) → V (20, sí) → V (15, no) → F (20, sí) → V (20, no) → F (15, sí) → F (15, no) → F no ve el "Y" no aísla partes aísla cada una 1 caso 2 casos hasta 4 casos
The same line of code demands a very different effort depending on the ruler. With two cases ("branches") you never separate the effect of age from that of the permit; only by isolating each condition do you find out whether an adult with no permit breaks the logic.

04 · the number that actually helpsWhat to watch and what not to chase

From here comes an uncomfortable conclusion for anyone who collects percentages: high coverage is necessary but not sufficient. 100 % line coverage coexists with untested logic; 100 % branch coverage is much harder to fake; MC/DC is nearly impossible without having thought through each decision. But none of them verifies that the output is correct: they measure what code ran, not whether the result was the one expected. A suite can run 100 % of the branches with empty asserts and never catch a single defect.

That is why chasing "100 %" as a goal usually makes the software worse: people write filler tests that touch lines without checking anything, just to move the number. Coverage works the other way around, as a gap detector: you read it to find which parts nobody has tested yet, and you decide whether that gap matters. An uncovered error handling path is an alarm; an uncovered logging branch, almost never. What to cover thoroughly is set by risk, not by the vanity of the percentage.

Coverage does not tell you whether your software is well tested. It tells you where it certainly is not. That is its true power.

So the next time someone celebrates a coverage number, ask the two questions that take it apart: coverage of what, lines or branches? And are the remaining gaps in code that does not matter, or in exactly the code that does? The answer separates a team that understands its metric from one that just watches it climb. At Qirava we treat coverage for what it is: a map of what is missing, not a trophy for what is to spare.

Sources

  1. ISO/IEC/IEEE 29119-4:2021. Software and systems engineering. Software testing. Part 4: Test techniques. International Organization for Standardization. (Defines the structural coverage criteria: statement, decision/branch and condition.)
  2. ISTQB (2018). Certified Tester Foundation Level Syllabus, v3.1. International Software Testing Qualifications Board. Section on white box techniques and statement and decision coverage.
  3. RTCA DO-178C / EUROCAE ED-12C (2011). Software Considerations in Airborne Systems and Equipment Certification. RTCA Inc. (Requires Modified Condition/Decision Coverage, MC/DC, for level A assurance software.)
  4. Myers, G. J., Sandler, C. & Badgett, T. (2011). The Art of Software Testing, 3rd ed. John Wiley & Sons. Chapter on white box testing and logic coverage.

Learn more about AI

See all Learn AI