6.1.6 Testing and evaluating programs

Testing a Program

Testing verifies that a program produces correct outputs for a range of inputs. A program is fit for purpose if it meets its specification correctly for all expected and unexpected inputs. Structured testing uses three categories of test data to expose different types of failure.

Test typeDescription
NormalTypical valid inputs that the program should handle in routine operation
BoundaryValues at the exact limits of valid ranges — tests whether edge conditions are handled correctly
ErroneousInvalid inputs the program should reject or handle gracefully without crashing

Worked Example: Testing a Score Validator

Program: accepts an exam score (0–100), prints "Valid" or "Invalid".

def validate_score(score):
    """Returns True if score is between 0 and 100 inclusive."""
    return score >= 0 and score <= 100

score_input = int(input("Enter score: "))
if validate_score(score_input):
    print("Valid")
else:
    print("Invalid")
Test typeInputExpected outputActual outputPass/Fail
Normal55ValidValidPass
Normal72ValidValidPass
Boundary0ValidValidPass
Boundary100ValidValidPass
Boundary-1InvalidInvalidPass
Boundary101InvalidInvalidPass
Erroneous-50InvalidInvalidPass
Erroneous200InvalidInvalidPass

Boundary values (0, 100, -1, 101) test whether the exact limits are handled correctly — a common off-by-one error would use > instead of >=, causing 0 to be rejected when it should be valid.

Evaluating Efficiency

A program is efficient if it solves its problem using as few operations as possible. For GCSE, efficiency is evaluated in terms of:

  • Number of comparisons: how many times does the program evaluate a condition?
  • Number of passes through a loop: how many iterations does the loop perform?
  • Memory use: does the program store more data than necessary?
# Less efficient: continues checking even after target is found
def contains(items, target):
    found = False
    for item in items:
        if item == target:
            found = True        # Sets flag but keeps looping
    return found

# More efficient: exits loop immediately on finding the target
def contains_efficient(items, target):
    for item in items:
        if item == target:
            return True         # Returns immediately — no unnecessary iterations
    return False

For a 1000-element list where the target is at index 0, the first version makes 1000 comparisons; the second makes 1.

 Key Takeaways

  • Test with normal (typical), boundary (limits) and erroneous (invalid) data.
  • Boundary testing is critical — off-by-one errors (> vs >=) only show up at the exact limits.
  • A test table records: input, expected output, actual output, pass/fail.
  • Efficiency: fewer comparisons and loop iterations = more efficient; early exit from loops improves best-case performance.