Session 3.5 — Introduction to White-box Testing & Control Flow Graphs
Module 3: Dynamic Testing — White-box Techniques | Duration: 1 hour
Learning Objectives
- Distinguish white-box testing from black-box testing and state when each is appropriate.
- Explain code coverage as a structural adequacy metric.
- Construct a Control Flow Graph (CFG) from source code, labelling nodes, edges, and decision points.
- Enumerate all distinct execution paths through a CFG.
What Is White-box Testing?
White-box testing (also called glass-box, structural, or logic-driven testing) designs test cases by looking inside the source code. Unlike black-box testing which derives tests from requirements alone, white-box testing uses the actual code structure — statements, branches, loops, and conditions — to determine what must be exercised.
Code that is never executed by any test can contain undetected defects. White-box techniques expose such gaps by measuring how much of the code structure has been covered.
Black-box validates external behaviour against requirements. White-box validates internal logic correctness. Both are essential for thorough quality assurance.
Developers during unit testing; test engineers during integration testing; security analysts during code audits. Source code access is required.
A test suite with 100% black-box coverage (all requirements tested) can still leave entire code branches unexecuted. White-box coverage metrics quantify and eliminate this hidden risk.
White-box vs Black-box Testing
- Based on requirements and specifications.
- No knowledge of internal code structure.
- Finds: missing functionality, incorrect outputs, interface errors.
- Techniques: BVA, Equivalence Class, Decision Table, CEG, OATS.
- Applied at: System and Acceptance testing levels.
- Based on source code structure and logic.
- Tester reads and analyses the code directly.
- Finds: dead code, missing branches, logic errors, unhandled paths.
- Techniques: Statement, Branch, Condition, Path coverage.
- Applied at: Unit and Integration testing levels.
A thorough black-box suite may never exercise a specific else branch that only triggers on malformed internal state. White-box coverage metrics reveal that gap immediately — the branch shows 0% execution in the coverage report.
Conversely, 100% statement coverage does not guarantee the system meets user requirements. Black-box tests are still needed to validate behaviour against the specification.
Code Coverage — Concept & Importance
Code coverage is a structural adequacy metric measuring what proportion of code elements have been executed by a test suite. It answers: "Are there parts of the code that no test touches?"
Coverage % = (Exercised elements ÷ Total elements) × 100. Industry targets typically range from 80% (enterprise) to 100% (safety-critical).
Untested paths hiding latent defects; dead code that can be removed safely; gaps in the test suite that need additional test cases.
That code is correct. A test can execute a statement without verifying its output. Coverage measures execution, not correctness.
| Level | Element Measured | Typical Use |
|---|---|---|
| Statement | Individual executable statements | Minimum baseline for unit tests |
| Branch | True and false outcome of every decision | Standard for most enterprise software |
| Condition | Each atomic boolean sub-expression | Complex compound conditions |
| Path | Complete execution sequences entry-to-exit | Safety-critical, protocol stacks |
Control Flow Graph (CFG)
A Control Flow Graph is a directed graph that models every possible execution path through a program. It is the primary tool for computing all structural coverage metrics.
| Component | Description |
|---|---|
| Node (Basic Block) | A maximal sequence of consecutive statements with no branching inside. Control enters at the top and exits at the bottom without stopping. |
| Edge | A directed arrow representing possible transfer of control between two nodes. |
| Entry Node | The single node where execution begins (function entry, program start). Has no incoming edges. |
| Exit Node | The node where execution terminates (return statement, end of function). Has no outgoing edges. |
| Decision Node | A node whose last statement is a conditional (if, while, for, switch). Has exactly two outgoing edges labelled T (true) and F (false). |
| Back Edge | An edge from a loop body back to the loop condition node. Indicates a cycle (loop repetition). |
- Group consecutive statements with no branching into a single node.
- Each
if,while,for, orswitchstatement forms its own decision node with separate T and F outgoing edges. - A loop creates a back-edge from the loop body back to the loop condition node.
- Label edges with condition outcome:
TandF. - Both branches of an
if-elseconverge at a join node after theelseblock. - An
ifwithout anelsestill has a false edge — it goes directly to the next statement after theifblock.
Loops introduce paths that require special attention:
- Zero iterations: The false edge from the loop condition is taken immediately (loop body never executes).
- One iteration: The true edge is taken once, then the false edge exits the loop.
- Multiple iterations: The true edge is taken repeatedly via the back-edge before the false edge exits.
CFG Worked Example — Grade Calculator
Consider the following Python-style function that determines a student's grade:
| Node | Code | Type |
|---|---|---|
| N1 | Function entry (parameters received) | Entry |
| N2 | if attendance < 75 | Decision |
| N3 | grade = "Detained" | Basic block |
| N4 | if marks >= 90 | Decision |
| N5 | grade = "O" | Basic block |
| N6 | elif marks >= 75 | Decision |
| N7 | grade = "A+" | Basic block |
| N8 | elif marks >= 60 | Decision |
| N9 | grade = "A" | Basic block |
| N10 | grade = "F" | Basic block |
| N11 | return grade | Exit |
| From | To | Condition |
|---|---|---|
| N1 | N2 | entry |
| N2 | N3 | T (attendance < 75) |
| N2 | N4 | F |
| N3 | N11 | sequential |
| N4 | N5 | T (marks ≥ 90) |
| N4 | N6 | F |
| N5 | N11 | sequential |
| N6 | N7 | T (marks ≥ 75) |
| N6 | N8 | F |
| N7 | N11 | sequential |
| N8 | N9 | T (marks ≥ 60) |
| N8 | N10 | F |
| N9 | N11 | sequential |
| N10 | N11 | sequential |
| Path | Nodes traversed | Scenario |
|---|---|---|
| P1 | N1 → N2(T) → N3 → N11 | Attendance < 75 → Detained |
| P2 | N1 → N2(F) → N4(T) → N5 → N11 | Attendance OK, marks ≥ 90 → Grade O |
| P3 | N1 → N2(F) → N4(F) → N6(T) → N7 → N11 | Attendance OK, 75 ≤ marks < 90 → Grade A+ |
| P4 | N1 → N2(F) → N4(F) → N6(F) → N8(T) → N9 → N11 | Attendance OK, 60 ≤ marks < 75 → Grade A |
| P5 | N1 → N2(F) → N4(F) → N6(F) → N8(F) → N10 → N11 | Attendance OK, marks < 60 → Grade F |
This CFG has 5 distinct paths, 4 decision nodes, and 14 edges. To achieve full path coverage, a minimum of 5 test cases are needed — one per path. To achieve full branch coverage, at least 5 tests are needed to cover both T and F of all 4 decision nodes (N2, N4, N6, N8).
Coverage Criteria Overview
White-box coverage criteria form a subsumption hierarchy: satisfying a stronger criterion automatically satisfies all weaker ones below it.
- Statement Coverage (SC) — Every executable statement executed at least once. Weakest criterion.
- Branch Coverage (BC) — Every branch outcome (T and F) of every decision taken at least once. Subsumes statement coverage.
- Condition Coverage (CC) — Each atomic condition in every decision evaluated to both T and F independently. Does not automatically subsume branch coverage.
- Branch/Condition Coverage (B/CC) — Both branch coverage and condition coverage satisfied simultaneously.
- Multiple Condition Coverage (MCC) — All combinations of true/false for each atomic condition in every decision tested. Thorough but exponentially expensive.
- Modified Condition/Decision Coverage (MC/DC) — Each condition independently affects the decision outcome. Industry standard for safety-critical systems (DO-178C, ISO 26262).
- Path Coverage (PC) — Every distinct path from entry to exit executed. Complete but infeasible for code with loops.
Most safety-critical standards require MC/DC as the minimum criterion — it achieves high confidence without exponential test counts.
For enterprise software, 80–100% branch coverage is the common target. Tools like JaCoCo, Istanbul, and Coverage.py measure this automatically.
Sessions 3.6 covers Statement and Branch coverage in depth. Session 3.7 covers Condition and MC/DC. Session 3.8 covers Path coverage and Cyclomatic Complexity.
Common Mistakes
100% statement coverage does not mean zero bugs. A test can execute every line without checking the output value.
Placing an
if condition and its body in the same CFG node hides the branch. Each decision must be its own node.An
if without an explicit else still has a false (fall-through) edge. That branch must be covered.A
while loop has at minimum two paths: loop body never executed (F at first check) and loop body executed at least once (T).Class Activity
You are given the following 14-line function that computes an employee's bonus:
- Identify all basic blocks and label them N1, N2, …
- Draw the CFG with directed edges labelled T/F.
- List all distinct paths from entry to exit.
- State the minimum number of test cases needed for full branch coverage.
- Design one test case per path and fill in the expected output.
- 3 marks: Correct node identification and basic block grouping.
- 3 marks: Accurate edge drawing with correct T/F labels.
- 2 marks: Complete and non-duplicate path enumeration.
- 2 marks: Correct test cases with expected outputs per path.
Exit Ticket
- What is the key difference between white-box and black-box testing in terms of test case design?
- In a CFG, what distinguishes a decision node from a basic block node?
- If 100% statement coverage is achieved, is 100% branch coverage also guaranteed? Justify your answer.
Summary & Assignment
White-box testing uses source code structure as the basis for test design. The Control Flow Graph models all execution paths and is the foundation for all structural coverage metrics. Coverage criteria range from statement (weakest) to path (strongest), and the appropriate level is chosen based on the risk and criticality of the software.