About

3.3 Paths, Decisions & Conditions

Theory: Paths, Decisions & Conditions

White box testing designs tests from the code, so first we need a way to see the code’s structure. This lesson turns a small method into a flowchart, names its paths, and then separates two words that students often mix up: decision and condition. Everything in 3.4 and 3.5 is built on these three ideas.

Turning Code into Paths

The professor used this small method from Myers’ book. We will keep using it for the coverage ladder in 3.4.

public void foo(int a, int b, int x) {
    if (a > 1 && b == 0) {      // decision 1
        x = x / a;
    }
    if (a == 2 || x > 1) {      // decision 2
        x = x + 1;
    }
}

Look at what it contains:

Think of driving through a town with two junctions. At each junction you can turn left or right, so there are 2×2=42 \times 2 = 4 different routes from one side of town to the other. A path is one complete route through the code, from start to end.

To name the routes, give each arrow of the flowchart a letter:

Diagram loads as you scroll.

Each path is now a short word made of letters:

PathDecision 1Decision 2Example test (a, b, x)
abdfalsefalse(1, 1, 1)
abefalsetrue(2, 1, 1)
acdtruefalse(3, 0, 3)
acetruetrue(2, 0, 3)

Tracing one test by hand

Take (3, 0, 3). Decision 1: 3>13 > 1 is true and 0=00 = 0 is true, so it is true (arrow c) and x = 3 / 3 = 1. Decision 2: 3=23 = 2 is false and x=1>1x = 1 > 1 is false, so it is false (arrow d). The path is acd, and the final value of x is 1.

The professor’s small warning: notice that foo has no return statement. So how do we see the result? The testing tool must let us read the final value of x. Without that, there is no observability (2.2), and we cannot compare the actual result with the expected one. The professor added this small warning in class; mentioning observability when a method returns nothing shows the examiner you are thinking like a tester.

Decision or Condition?

Students found these two words hard, so the professor used an ATM. It gives money only if:

Each small check is a condition: it is either true or false. The final choice, give money or not, is the decision: also true or false, but it is the answer the whole if produces.

if (pinCorrect && balanceEnough) giveMoney();

Diagram loads as you scroll.

In code the distinction is easy to spot:

So foo has 2 decisions and 4 conditions: a > 1, b == 0, a == 2 and x > 1.

A condition is one small true/false check inside a decision. A decision is the final true/false outcome of the whole if. A decision made of nn conditions joined by &&/|| still has only two outcomes.

Common mistake: counting the number of conditions as the number of decisions. if (a > 1 && b == 0) is one decision with two conditions. Count decisions by counting ifs (and other branching statements); count conditions by counting the small comparisons inside them.


Worked Examples

Exercise 1

In if (age >= 18 && hasTicket), how many decisions and how many conditions are there?

Show solution ↓
Solution

The if statement is the decision point, so there is 1 decision. Inside the brackets there are two small true/false checks joined by &&: age >= 18 and hasTicket. So there are 2 conditions.

Exercise 2

For foo above, trace each test and give its path (as letters) and the final value of x:

  1. (1, 1, 1)
  2. (2, 1, 1)
  3. (2, 0, 4)
  4. (4, 0, 8)
Show solution ↓
Solution

For each test evaluate decision 1, update x if it is true (integer division), then evaluate decision 2 with the new x.

  1. (1, 1, 1): 1>11 > 1 is false, so decision 1 is false (arrow b). Decision 2: 1=21 = 2 false, 1>11 > 1 false, so false (arrow d). Path abd, final x = 1.
  2. (2, 1, 1): 2>12 > 1 true but 1=01 = 0 false, so decision 1 is false (b). Decision 2: a=2a = 2 is true, so true (e), x = 1 + 1. Path abe, final x = 2.
  3. (2, 0, 4): decision 1 true (c), x = 4 / 2 = 2. Decision 2: a=2a = 2 true, so true (e), x = 3. Path ace, final x = 3.
  4. (4, 0, 8): decision 1 true (c), x = 8 / 4 = 2. Decision 2: 4=24 = 2 false, but x=2>1x = 2 > 1 true, so true (e), x = 3. Path ace, final x = 3.
Exercise 3

Trace foo(3, 0, 5). Then explain what would change if the division were ordinary (non-integer) division, and why this matters for a tester.

Show solution ↓
Solution

Decision 1: 3>13 > 1 true and 0=00 = 0 true, so true: x = 5 / 3. In Java this is integer division, so x = 1. Decision 2: 3=23 = 2 false and 1>11 > 1 false, so false. The path is acd and the final value is x = 1.

With ordinary division, x=5/3≈1.67x = 5/3 \approx 1.67, and then x>1x > 1 would be true, so decision 2 would be true and the path would be ace with x≈2.67x \approx 2.67.

So the same input follows a different path depending on how division works. A tester who forgets integer division will predict the wrong expected result and either report a false bug or design a test that does not cover the path they intended.

Exercise 4

Find one test (a, b, x) for each of the paths abd and acd of foo. Show the check.

Show solution ↓
Solution

Path abd needs both decisions false.

  • Decision 1 false: make a>1a > 1 false, e.g. a=1a = 1.
  • Decision 2 false: we need a≠2a \neq 2 (true for a=1a = 1) and x≤1x \le 1. Take x=1x = 1.

Test (1, 1, 1). Check: 1>11 > 1 false → b; 1=21 = 2 false, 1>11 > 1 false → d ✓.

Path acd needs decision 1 true and decision 2 false.

  • Decision 1 true: a>1a > 1 and b=0b = 0.
  • Decision 2 false: a≠2a \neq 2, so a≥3a \ge 3, and the new x=x/ax = x / a must be at most 1.

Take a=3a = 3, b=0b = 0, x=3x = 3: test (3, 0, 3). Check: true → c, x = 3 / 3 = 1; 3=23 = 2 false, 1>11 > 1 false → d ✓.

Exercise 5

How many decisions and how many conditions does this code have?

if (age < 12 || age >= 65) {
    price = 50;
} else if (isStudent && hasCard) {
    price = 80;
}
Show solution ↓
Solution

Count the decision points: the first if and the else if each produce their own true/false outcome, so there are 2 decisions.

Count the small checks inside the brackets: age < 12, age >= 65, isStudent, hasCard, so there are 4 conditions.

Exercise 6

The method foo returns nothing. What problem does this create for testing, and which testability property is involved?

Show solution ↓
Solution

A test must compare the actual result with the expected result. Since foo has no return statement and x is a local variable, a caller cannot see the final value of x at all. Unless the testing tool lets us read that final value, there is nothing to compare, and the test cannot pass or fail.

The property involved is observability (2.2): how easily we can see the internal state and output of the software under test. Low observability makes a program hard to test.