Statement Coverage
Execute every executable statement in the code at least once. It’s the most basic white box metric — a necessary starting point, not a sufficient end point.
What it is
Statement coverage (also called line coverage) measures whether each executable statement in the code has been executed at least once during testing. It’s expressed as a percentage: statements executed ÷ total statements × 100.
It answers the question: is there any code we haven’t run at all? It doesn’t answer: have we tested all the logic?
Measuring it
Statement coverage is measured by code coverage tools (Istanbul/nyc for JavaScript, JaCoCo for Java, Coverage.py for Python, etc.). These tools instrument the code and report which lines were hit during your test suite.
Worked example
Consider this function that calculates a discount:
function getDiscount(user, total) {
let discount = 0; // S1 — always executed
if (user.isMember) { // S2 — decision point
discount = 0.10; // S3 — only if member
if (total > 100) { // S4 — only if member
discount = 0.15; // S5 — only if member AND total > 100
}
}
return discount; // S6 — always executed
}
| Test input | Statements hit | Coverage |
|---|---|---|
| Non-member, total = 50 | S1, S2, S6 | 50% |
| Member, total = 50 | S1, S2, S3, S4, S6 | 83% |
| Member, total = 150 | S1, S2, S3, S4, S5, S6 | 100% |
Notice: a single test with member, total = 150 achieves 100% statement coverage. But it doesn’t test the non-member case at all.
The limits of statement coverage
- 100% statement coverage doesn’t mean all logic is tested. A single test that executes all statements may never test the false branch of any decision.
- It doesn’t find missing code. If a required validation was simply never written, it won’t show up as uncovered.
- It doesn’t test combinations. Each statement is hit once — not every combination of conditions.
Coverage is a floor, not a ceiling. 100% statement coverage is a minimum bar, not proof the code is correct. Teams that treat it as a quality target are measuring the wrong thing.
ISTQB mapping
| Ref | Topic |
|---|---|
| 4.3.1 | Statement Testing and Coverage |
| FL-4.3.1 K2 | Explain statement testing and statement coverage |
| FL-4.3.1 K2 | Explain the reasons for measuring statement coverage |
vs branch coverage
Branch coverage is stronger — it requires both the true AND false outcomes of every decision to be exercised. Statement coverage subsumes statement execution; branch coverage subsumes statement coverage. For most production code, branch coverage is the minimum target.
Practice this technique: Try Test Lead Practice 07 — Test coverage gaps.
Try It — Calculate statement coverage
A NZ GST validation function has 7 executable statements (S1–S7). A test suite runs three tests. Work out which statements each test hits, then calculate coverage.
function validateGST(amount, isGSTRegistered) {
if (amount <= 0) { // S1
return 'Invalid amount'; // S2
}
let gst = 0; // S3
if (isGSTRegistered) { // S4
gst = amount * 0.15; // S5
}
let total = amount + gst; // S6
return total; // S7
}
| Test | amount | isGSTRegistered | Statements hit | % coverage |
|---|---|---|---|---|
| Test 1 | -5 | false | ||
| Test 2 | 100 | false | ||
| Test 3 | 200 | true |
After all 3 tests, what is the combined statement coverage? %
Answers
| Test | Statements hit | Coverage |
|---|---|---|
| Test 1 (amount=-5) | S1, S2 | 29% (2/7) |
| Test 2 (amount=100, not registered) | S1, S3, S4, S6, S7 | 71% (5/7) |
| Test 3 (amount=200, registered) | S1, S3, S4, S5, S6, S7 | 86% (6/7) |
Combined coverage after all 3 tests: 100% — S1+S2 (test 1) + S3+S4+S5+S6+S7 (tests 2&3) = all 7 statements hit.
But notice: S2 is only hit once (when amount is negative). Tests 2 and 3 never go back into the false branch of S4. 100% statement coverage does NOT mean all branches were tested. That requires branch coverage.