4.6 White-Box Coverage
Key Takeaways
- White-box (structure-based) techniques derive tests from internal structure such as source code and control flow.
- Statement coverage measures the percentage of executable statements exercised: statements run ÷ total statements × 100%.
- Branch coverage measures the percentage of decision outcomes (branches) exercised: branches taken ÷ total branches × 100%.
- 100% branch coverage guarantees 100% statement coverage, but not the reverse — branch coverage is the stronger criterion.
- High structural coverage shows what code ran, not whether the requirements are correct or complete.
Core Idea
White-box testing (also structure-based or structural testing) derives tests from the internal structure of a component — most commonly the source code and its control flow. It answers a different question from black-box testing: not 'does the system behave as specified?' but 'how much of the code that exists has my test suite actually run?'
At CTFL Foundation level the syllabus covers exactly two structural techniques and their coverage measures:
- Statement testing / statement coverage — coverage item is the executable statement.
- Branch testing / branch coverage — coverage item is the branch (a decision outcome).
White-box techniques are most associated with component (unit) testing, where developers have the code and can run coverage tools, but they are also applied at integration and even system level (e.g. menu or API path coverage). They are valuable because they reveal code that no test exercises — untested error handlers, dead defensive branches, or whole functions that requirements-based tests never reach. They cannot, however, find missing functionality: if a requirement was never implemented, there is no code to cover, so structural coverage stays silent about the gap.
Statement Coverage — Worked Example
Statement coverage is the proportion of executable statements run by the test suite:
Statement coverage % = (executable statements exercised
÷ total executable statements) × 100
Worked example — a function with 20 executable statements where a test suite runs 15 of them:
15 ÷ 20 × 100 = 75% statement coverage
To reach 100%, you must add tests until every one of the 20 statements is executed at least once. Consider:
1 read amount
2 if amount > 100
3 discount = 10
4 print discount
One test with amount = 150 runs statements 1, 2, 3, 4 — that is 100% statement coverage with a single test, because the path passes through every line. Notice what that single test did not do: it never executed the case where the if is false. Statement coverage is satisfied even though the 'amount ≤ 100' situation was never tried — which is precisely why a stronger criterion exists.
Branch Coverage and Its Value
Branch coverage is the proportion of decision outcomes (branches) exercised. Every if, while, or for decision has two outcomes — true and false — and each is a branch:
Branch coverage % = (branches exercised
÷ total branches) × 100
Worked example — a component with three binary decisions has 3 × 2 = 6 branch outcomes. If a suite exercises 5 of the 6:
5 ÷ 6 × 100 ≈ 83% branch coverage
Return to the discount snippet: the single test with amount = 150 took the true branch of the if but never the false branch, so it achieved 100% statement coverage but only 50% branch coverage (1 of 2 outcomes). A second test with amount = 50 forces the false branch, reaching 100%.
This demonstrates the key relationship the exam tests: 100% branch coverage guarantees 100% statement coverage, but 100% statement coverage does NOT guarantee 100% branch coverage. Branch coverage is therefore the stronger criterion. The value of white-box testing is that it quantifies untested code and catches gaps black-box tests cannot see — but its limitation is equally important: even 100% branch coverage proves only that the code ran, never that the code is correct or that the requirements were fully implemented.
The Value and Limits of White-Box Testing
The CTFL syllabus is explicit about why structural testing earns its place and where it falls short, and the exam tests both sides.
Value:
- It measures thoroughness against the code that exists, giving an objective, tool-supported number that black-box techniques cannot provide.
- It finds untested code — error handlers, defensive branches, and entire functions that requirements-based tests never reach.
- It exposes unexpected or unreachable code (dead code) and gaps left by the specification-based suite.
- It complements black-box testing: black-box tells you whether the specified behaviour works; white-box tells you how much of the implementation your tests actually drove.
Limits:
- It cannot detect missing functionality. If a requirement was never coded, there is no statement or branch to cover, so 100% coverage says nothing about the omission.
- It says nothing about correctness: a fully covered function can still compute the wrong answer.
- High coverage can breed false confidence, which is why coverage targets are a floor, not a goal in themselves.
| Criterion | Coverage item | Subsumes | Notes |
|---|---|---|---|
| Statement coverage | Executable statement | — | Weaker; can be 100% with a decision untested one way |
| Branch coverage | Decision outcome | Statement coverage | Stronger; 100% branch ⇒ 100% statement |
For the exam, be ready to: compute statement coverage (statements run ÷ total) and branch coverage (branches taken ÷ total); state that branch coverage subsumes statement coverage but not the reverse; and explain that structural coverage is a thoroughness measure that neither proves correctness nor reveals missing requirements. White-box testing is overwhelmingly a component-level activity, but the same coverage thinking applies to higher levels such as API-path or menu-path coverage.
A function has 20 executable statements. A test suite executes 15 of them. What is the statement coverage?
A component has three binary decisions. A suite exercises five of the six decision outcomes. What is the branch coverage?
Which statement about statement and branch coverage is correct?