How do we test software systematically and find the cause of a defect efficiently?
Design test cases using normal, boundary and erroneous data, distinguish levels of testing, and apply systematic debugging techniques
A focused answer to the H2 Computing outcome on testing and debugging. Choosing normal, boundary and erroneous test data, unit and integration and system testing, black-box versus white-box, and systematic debugging.
Reviewed by: AI editorial process; not yet individually human-reviewed
Have a quick question? Jump to the Q&A page
Jump to a section
What this dot point is asking
SEAB wants you to design test cases using normal, boundary and erroneous data, distinguish the levels and styles of testing, and apply systematic debugging. The central idea is that testing is deliberate, not random: you choose inputs that are most likely to expose faults, and when a test fails you locate the cause methodically rather than by guesswork.
The answer
Choosing test data
Good test data deliberately probes where bugs hide. Three categories:
- Normal (valid) data - typical values well inside the acceptable range, confirming the program works for ordinary input.
- Boundary data - values at the edges of the valid range and just outside it (for a range 0 to 100: test 0, 100, and -1, 101). Boundaries catch the common off-by-one errors.
- Erroneous (invalid) data - values the program should reject: out of range, wrong type, empty input. These check that the program fails safely.
A thorough test set includes all three.
Levels of testing
Testing happens at increasing scope:
- Unit testing - one component (a function or class) in isolation.
- Integration testing - components working together, catching interface faults.
- System testing - the whole system against its requirements.
- Acceptance testing - the client confirms it meets their needs.
Black-box versus white-box
- Black-box testing derives test cases from the specification, ignoring internal code - testing what the program should do.
- White-box testing uses knowledge of the code structure to exercise specific paths, branches and conditions - testing how it does it (aiming for good code coverage).
The two are complementary: black-box checks behaviour, white-box checks internal logic.
Systematic debugging
When a test fails, locate the fault methodically:
- Reproduce the failure reliably with a minimal input.
- Locate the region - use print statements or a debugger's breakpoints, and bisection: check the program state at a midpoint, then halve the suspect region repeatedly.
- Hypothesise the cause, then test the hypothesis by inspecting variables or stepping through.
- Fix and retest, including a regression test so the bug cannot silently return.
This beats random edits, which often introduce new faults.
Examples in context
Example 1. Testing a date validator. A function checking calendar dates is tested with normal dates, boundaries (the 28th, 29th, 30th, 31st around month ends, and leap-year February 29), and erroneous inputs (month 13, day 0, text). The boundary cases around month lengths are exactly where validation bugs cluster, so they earn their place in the test set.
Example 2. Bisecting a regression. When a previously working report suddenly produces wrong totals, a developer uses bisection on the version history (and on the data pipeline) to find the change that introduced the fault, checking the midpoint each time. Halving the search space turns a vast hunt into a handful of checks.
Try this
Q1. For a field accepting ages 18 to 65 inclusive, give one boundary value and one erroneous value to test. [2 marks]
- Cue. Boundary: 18 or 65 (and just-beyond 17 or 66); erroneous: a negative age, 0, or a non-numeric input.
Q2. Distinguish between unit testing and integration testing. [2 marks]
- Cue. Unit testing checks a single component in isolation; integration testing checks that components work correctly together, catching interface faults.
Q3. Describe one systematic technique for locating a bug. [1 mark]
- Cue. Bisection - check the program state at a midpoint and repeatedly halve the suspect region until the faulty line is isolated.
Exam-style practice questions
Practice questions written in the style of SEAB exam questions on this dot point, with worked answer explainers. The year tag is the paper they imitate, not the source.
Original6 marksA function accepts an exam mark from 0 to 100 inclusive and classifies it. Design a set of test data covering normal, boundary and erroneous cases, giving the value, its category and the expected behaviour for each.Show worked answer →
Choose data that exercises the valid range, its edges, and invalid inputs:
| Value | Category | Expected behaviour |
|---|---|---|
| 55 | normal | accepted and classified normally |
| 0 | boundary (lower) | accepted (lowest valid mark) |
| 100 | boundary (upper) | accepted (highest valid mark) |
| -1 | erroneous (just below) | rejected as invalid |
| 101 | erroneous (just above) | rejected as invalid |
| "abc" | erroneous (wrong type) | rejected as invalid |
Normal data lies well inside the valid range; boundary data sits exactly at the limits (0 and 100) and just outside (-1, 101); erroneous data is invalid (out of range or wrong type). Boundaries and just-beyond values catch the most common off-by-one bugs.
Markers reward at least one normal value, both boundaries (0 and 100), and erroneous values including out-of-range and wrong-type inputs, each with the expected result.
Original5 marks(a) Distinguish between unit testing and integration testing. (b) Distinguish between black-box and white-box testing. (c) Describe one systematic technique for locating the cause of a bug.Show worked answer →
(a) Unit testing tests an individual component (such as a single function) in isolation. Integration testing tests that separately working components interact correctly when combined - it catches interface and interaction faults that unit tests miss.
(b) Black-box testing chooses test cases from the specification without knowing the internal code - testing what it should do. White-box testing uses knowledge of the code's structure to exercise specific paths, branches and conditions - testing how it does it.
(c) A systematic technique: divide and conquer / bisection - narrow the region containing the fault by checking the program state at a midpoint (using print statements or a debugger's breakpoints), then repeatedly halving the suspect region until the faulty line is isolated. Other valid answers: stepping through with a debugger, or forming and testing a hypothesis about the cause.
Markers reward the unit-versus-integration distinction, the black-box (spec) versus white-box (code structure) distinction, and a genuine systematic debugging method.
Related dot points
- Describe the stages of the software development lifecycle and compare the waterfall and agile (iterative) approaches
A focused answer to the H2 Computing outcome on the software development lifecycle. The analysis, design, implementation, testing, deployment and maintenance stages, and the contrast between the waterfall and agile iterative models.
- Use Python selection, iteration and functions with parameters and return values to structure a solution, applying scope correctly
A focused answer to the H2 Computing outcome on Python control flow. Selection with if and elif, iteration with for and while, defining functions with parameters and return values, and local versus global scope.
- Handle runtime errors with try and except, and read from and write to text files safely in Python
A focused answer to the H2 Computing outcome on exceptions and files. The try, except, else and finally blocks, raising exceptions, and reading and writing text files safely with the with statement.
- Apply decomposition, modularity and abstraction to structure software, explaining the benefits for maintenance and reuse
A focused answer to the H2 Computing outcome on decomposition and abstraction. Breaking problems into modules, the role of interfaces, information hiding through abstraction, and the benefits for maintainability and reuse.
- Analyse the time and space complexity of an algorithm using Big-O notation, and compare common growth rates
A focused answer to the H2 Computing outcome on algorithmic complexity. Big-O notation, deriving the order of growth from loops and recursion, the common complexity classes, and the trade-off between time and space.