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.

Core principle
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.
Complements black-box
Black-box validates external behaviour against requirements. White-box validates internal logic correctness. Both are essential for thorough quality assurance.
Who applies it?
Developers during unit testing; test engineers during integration testing; security analysts during code audits. Source code access is required.
Key insight

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

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.
White-box Testing
  • 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.
Why both are necessary

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 formula
Coverage % = (Exercised elements ÷ Total elements) × 100. Industry targets typically range from 80% (enterprise) to 100% (safety-critical).
What coverage reveals
Untested paths hiding latent defects; dead code that can be removed safely; gaps in the test suite that need additional test cases.
What coverage does NOT prove
That code is correct. A test can execute a statement without verifying its output. Coverage measures execution, not correctness.
Coverage granularity levels
LevelElement MeasuredTypical Use
StatementIndividual executable statementsMinimum baseline for unit tests
BranchTrue and false outcome of every decisionStandard for most enterprise software
ConditionEach atomic boolean sub-expressionComplex compound conditions
PathComplete execution sequences entry-to-exitSafety-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.

CFG components
ComponentDescription
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.
EdgeA directed arrow representing possible transfer of control between two nodes.
Entry NodeThe single node where execution begins (function entry, program start). Has no incoming edges.
Exit NodeThe node where execution terminates (return statement, end of function). Has no outgoing edges.
Decision NodeA node whose last statement is a conditional (if, while, for, switch). Has exactly two outgoing edges labelled T (true) and F (false).
Back EdgeAn edge from a loop body back to the loop condition node. Indicates a cycle (loop repetition).
Rules for drawing a CFG
  1. Group consecutive statements with no branching into a single node.
  2. Each if, while, for, or switch statement forms its own decision node with separate T and F outgoing edges.
  3. A loop creates a back-edge from the loop body back to the loop condition node.
  4. Label edges with condition outcome: T and F.
  5. Both branches of an if-else converge at a join node after the else block.
  6. An if without an else still has a false edge — it goes directly to the next statement after the if block.
Loop handling in CFGs

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:

def calculate_grade(marks, attendance): # N1 – entry if attendance < 75: # N2 – decision grade = "Detained" # N3 else: if marks >= 90: # N4 – decision grade = "O" # N5 elif marks >= 75: # N6 – decision grade = "A+" # N7 elif marks >= 60: # N8 – decision grade = "A" # N9 else: grade = "F" # N10 return grade # N11 – exit
Node identification
NodeCodeType
N1Function entry (parameters received)Entry
N2if attendance < 75Decision
N3grade = "Detained"Basic block
N4if marks >= 90Decision
N5grade = "O"Basic block
N6elif marks >= 75Decision
N7grade = "A+"Basic block
N8elif marks >= 60Decision
N9grade = "A"Basic block
N10grade = "F"Basic block
N11return gradeExit
Edge list
FromToCondition
N1N2entry
N2N3T (attendance < 75)
N2N4F
N3N11sequential
N4N5T (marks ≥ 90)
N4N6F
N5N11sequential
N6N7T (marks ≥ 75)
N6N8F
N7N11sequential
N8N9T (marks ≥ 60)
N8N10F
N9N11sequential
N10N11sequential
All distinct paths (entry to exit)
PathNodes traversedScenario
P1N1 → N2(T) → N3 → N11Attendance < 75 → Detained
P2N1 → N2(F) → N4(T) → N5 → N11Attendance OK, marks ≥ 90 → Grade O
P3N1 → N2(F) → N4(F) → N6(T) → N7 → N11Attendance OK, 75 ≤ marks < 90 → Grade A+
P4N1 → N2(F) → N4(F) → N6(F) → N8(T) → N9 → N11Attendance OK, 60 ≤ marks < 75 → Grade A
P5N1 → N2(F) → N4(F) → N6(F) → N8(F) → N10 → N11Attendance OK, marks < 60 → Grade F
Key observation

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.

Hierarchy (weakest to strongest)
  1. Statement Coverage (SC) — Every executable statement executed at least once. Weakest criterion.
  2. Branch Coverage (BC) — Every branch outcome (T and F) of every decision taken at least once. Subsumes statement coverage.
  3. Condition Coverage (CC) — Each atomic condition in every decision evaluated to both T and F independently. Does not automatically subsume branch coverage.
  4. Branch/Condition Coverage (B/CC) — Both branch coverage and condition coverage satisfied simultaneously.
  5. Multiple Condition Coverage (MCC) — All combinations of true/false for each atomic condition in every decision tested. Thorough but exponentially expensive.
  6. Modified Condition/Decision Coverage (MC/DC) — Each condition independently affects the decision outcome. Industry standard for safety-critical systems (DO-178C, ISO 26262).
  7. Path Coverage (PC) — Every distinct path from entry to exit executed. Complete but infeasible for code with loops.
Industry standard
Most safety-critical standards require MC/DC as the minimum criterion — it achieves high confidence without exponential test counts.
Practical baseline
For enterprise software, 80–100% branch coverage is the common target. Tools like JaCoCo, Istanbul, and Coverage.py measure this automatically.
Sessions ahead
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

Equating coverage with correctness
100% statement coverage does not mean zero bugs. A test can execute every line without checking the output value.
Merging decision and body into one node
Placing an if condition and its body in the same CFG node hides the branch. Each decision must be its own node.
Missing the implicit else
An if without an explicit else still has a false (fall-through) edge. That branch must be covered.
Ignoring loop paths
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:

def compute_bonus(years, rating, sales): if years < 1: return 0 bonus = 0 if rating == "A": bonus += 5000 if sales > 100000: bonus += 3000 if years >= 5: bonus += 2000 return bonus
  1. Identify all basic blocks and label them N1, N2, …
  2. Draw the CFG with directed edges labelled T/F.
  3. List all distinct paths from entry to exit.
  4. State the minimum number of test cases needed for full branch coverage.
  5. Design one test case per path and fill in the expected output.
Evaluation rubric (10 marks)
  • 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

  1. What is the key difference between white-box and black-box testing in terms of test case design?
  2. In a CFG, what distinguishes a decision node from a basic block node?
  3. 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.

Assignment: Take any function of 15–25 lines from your mini-project. (1) Draw its complete CFG with labelled nodes and edges. (2) Enumerate all distinct paths. (3) Identify the minimum set of test inputs needed for 100% branch coverage. Submit the CFG diagram and test case table with expected outputs.