Software Design & Development · Testing

SDD16 — Testing: Test Plans and Error Types

📅 Mon 21 Sep 2026 · P1+P2 (double)
~120 minutes
Learning intentions
Success criteria
Warm up — recap from SDD15
Answer all three questions, then check your answers.
Question 1
Using the six-item itemRecords array, which item has the lowest .stock value?
Question 2
When adapting a standard algorithm to work on an array of records, what single element usually needs to change?
Question 3
Why does a minimum-finding-on-records algorithm typically track the record's index, rather than just the field value?

Key vocabulary

Test plan
A structured document listing test cases, test data, expected results, and actual results, used to check a program meets its requirements.
Test case
One specific scenario being tested, tied to a particular functional requirement and a particular piece of test data.
Syntax error
A violation of the programming language's grammar rules, detected before the program ever runs.
Execution error
An error that occurs while the program is running, causing it to crash partway through (also called a runtime error).
Logic error
A mistake that lets the program run to completion without crashing, but produces an incorrect result.

Test plans and error types

Why test at all?

Testing exists to answer one specific question: does the program actually do what it was supposed to do, as identified back in SDD2's analysis stage (its functional requirements — inputs, processes, and outputs)? A program that runs without crashing is not the same thing as a program that is correct — testing is how the gap between "runs" and "correct" gets closed. A comprehensive test plan, as required at Higher, must show that every functional requirement has genuinely been checked, not just that the program was run once and seemed to work.

Building a test plan

A test plan is a table. Each row is one test case, and typically includes: a description of what is being tested (tied to a specific functional requirement), the test data used, the expected result (worked out by hand, before running the program), the actual result (what the program genuinely produced when run), and a pass/fail outcome. A test plan with only one or two rows rarely counts as comprehensive — every distinct functional requirement needs at least one test case, and most requirements need several, covering different kinds of test data.

Three kinds of test data

National 5 already introduced three categories of test data, and Higher continues to expect all three in a comprehensive test plan: normal data (a typical, valid input the program should clearly handle correctly), extreme data (a valid input right at the edge of what's acceptable — a boundary value), and erroneous data (an invalid input that should be rejected or handled safely, not silently accepted as if it were valid). Testing only with normal data is not comprehensive — it's exactly the extreme and erroneous cases that most often expose a hidden logic error.

Three kinds of error

When a test case fails, it helps to know which of three distinct kinds of error caused it. A syntax error breaks the language's own grammar rules — a missing colon, an unmatched bracket — and is caught before the program ever starts running at all, usually flagged immediately by the editor or interpreter. An execution error (also called a runtime error) happens once the program is already running, causing it to crash partway through — dividing by zero, or accessing an array index that doesn't exist. A logic error is the most dangerous of the three precisely because it produces no error message whatsoever: the program runs to completion successfully, but the result it produces is simply wrong, because the underlying logic (a comparison, a calculation, an initial value) was mistaken.

National 5 recap

Normal, extreme, and erroneous test data were first introduced at National 5 and are carried forward unchanged at Higher — what's new here is applying them systematically inside a full, traceable test plan, rather than testing a program informally with whatever data happens to come to mind.

Syntax error
Breaks the language's grammar. Caught before the program runs at all.def isEven(number) return number % 2 == 0
Execution error
Program starts running, then crashes partway through.total / count # count is 0 ZeroDivisionError
Logic error
Program runs to completion, but the result is wrong — no error message at all.smallest = 0 # should be # array[0]

Worked examples

Example 1 — Identifying a syntax error
1
def isEven(number)
    return number % 2 == 0
2
The function definition line is missing its colon (:) at the end — this breaks Python's grammar rules for defining a function.
Confirmed by an actual Python run: SyntaxError: expected ':'. This is caught immediately — the program never gets a chance to run at all, since it isn't valid Python.
Example 2 — Identifying an execution error
1
def calculateAverage(total, count):
    return total / count

result = calculateAverage(100, 0)
2
This code is syntactically valid — it starts running normally — but dividing by 0 is not a valid mathematical operation.
Confirmed by an actual Python run: ZeroDivisionError: division by zero. The program crashes while running, not before — this is an execution error, not a syntax error.
Example 3 — Identifying a logic error
1
Recalling SDD14's findMinimum misconception — initialising smallest to 0 instead of array[0]:
def findMinimumBuggy(array):
    smallest = 0
    for i in range(len(array)):
        if array[i] < smallest:
            smallest = array[i]
    return smallest

stockLevels = [30, 45, 20, 15, 60, 18]
result = findMinimumBuggy(stockLevels)
2
This code has no syntax problems, and it doesn't crash — every element is a valid comparison against smallest. It runs to completion successfully.
result is 0 — confirmed by an actual Python run — even though 0 does not appear anywhere in stockLevels and the true minimum is 15. No error message is ever produced; this is a logic error, and the only way to catch it is by checking the result against a hand-worked expected value, exactly what a test plan is for.
Example 4 — A comprehensive test plan for calculateChange()
1
Testing SDD8's calculateChange(amountPaid, price) against normal, extreme, and erroneous data:
Test data typeTest dataExpected resultActual resultPass/Fail
NormalcalculateChange(100, 75)2525Pass
Extreme (exact payment)calculateChange(75, 75)00Pass
Erroneous (insufficient payment)calculateChange(50, 75)-25, or a rejection?-25Fail?
The first two rows pass — confirmed by an actual Python run. The third row is deliberately marked with a question mark: the function runs and returns -25 without crashing, but whether a negative change value counts as a "pass" or a "fail" depends entirely on whether SDD2's original functional requirements said anything about handling insufficient payment. If they didn't, this test case has just revealed a genuine gap in the requirements themselves, not just a bug in the code.
Now you try
Classify each of the following as a syntax error, an execution error, or a logic error: (a) forgetting a closing bracket on a function call; (b) accessing itemNames[10] when itemNames only has 6 elements; (c) using > instead of < in a minimum-finding algorithm.
⚠️ Common mistakes — examiner feedback
📝 Exam tip

When asked to identify an error type from a code snippet or description, check first whether the program would even start running (syntax error), then whether it would crash partway through (execution/runtime error), and only then consider whether it produces a wrong-but-uncrashed result (logic error) — working through the three in this order avoids second-guessing. When designing test data, always state explicitly which category (normal, extreme, erroneous) each test case belongs to — examiners check for this classification, not just the data itself.

Task Set A — Core questions

Task Set A — Core questions
Work through all questions, then check your answers.
Question 1
What term describes an error that is caught before a program ever starts running, because it breaks the language's own grammar rules?
Question 2
What term describes an error that causes a program to crash partway through running, such as dividing by zero?
Question 3
What term describes an error where a program runs to completion without crashing, but produces a wrong result?
Question 4
calculateAverage(100, 0) raises ZeroDivisionError. What kind of error is this?
Question 5
Which category of test data is a valid input right at the edge of what's acceptable, such as the very last valid index of an array?
Question 6
A test case for a function that only accepts positive numbers is run with -5 as input. Which category of test data does this represent?
Question 7
Explain why a logic error is often considered the most dangerous of the three error types, and how a test plan helps catch one. (4 marks)
Question 8
findMinimumBuggy(stockLevels) (with smallest wrongly initialised to 0) returns 0 instead of the true minimum 15, with no error message. What kind of error is this?
Question 9
Design one normal, one extreme, and one erroneous test case for calculateChange(amountPaid, price), stating the test data and expected result for each. (4 marks)
Question 10
Explain why a test plan's "expected result" must be worked out before the program is run, not after. (3 marks)

Task Set B — Extension

Task Set B — Extension · Beyond the specification
Longer written answers — no auto-check. Discuss your answers with your teacher.
Extension 1
SDD15's findMinStockRecord and countLowStock functions were built without an explicit test plan. Design two edge-case test cases that would be worth adding for these functions specifically.
Extension 2
Research how a code editor such as PyCharm helps detect syntax errors before a program is even run. Suggest why execution errors and logic errors are harder for an editor to detect automatically than syntax errors.
Extension 3
SDD17 will cover debugging techniques (dry runs, trace tables, breakpoints, watchpoints). Suggest how these techniques relate to a test plan that has already revealed a failing test case.
📁 File this in OneNote under:
Higher Computing Science → Software Design & Development → SDD16
📌 Teacher notes — not for pupils

Double period. First lesson of the Testing/Evaluation portion of the course — the entire Implementation strand (SDD5–SDD15) is now complete.

Suggested timing: 5 min warm-up + vocab · 25 min notes + error-grid/N5-recap walkthrough · 35 min examples 1–4 including the deliberately ambiguous Example 4 row 3 (discuss as a class before revealing "the answer") · 10 min "now you try" · 40 min Task Set A · Task Set B as homework/extension.

Key misconception: pupils often think "the program ran without an error message" is sufficient evidence of correctness — Example 3 (the SDD14 logic-error callback) and Task Set A Q8 both deliberately reuse the corrected SDD14 bug to hammer this home, since it's a real bug this course itself only caught by cross-checking against an actual Python run.

Example 4's third test-plan row is intentionally left ambiguous (marked "Fail?") — this is a genuine teaching opportunity about traceability back to SDD2's functional requirements, not a mistake in the lesson. Use it to discuss: what should happen if a functional requirement was never actually specified for this case?

SQA command words covered: explain, describe, design (test data/test cases), identify (error type).