My tests passed on the obvious inputs and the app still broke in production. The obvious inputs are the easy ones. Bugs hide at the edges, so the skill is picking the cases at the boundary.
What you will see
>>>
About 2 min of reading
Quick check
Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.
Try It
Write the boundary and edge cases for a grading rule and let them expose an off-by-one.
1
A grade function with a boundary bug: it uses > where it should use >= at 60.
def grade(score):
if score > 60:
return "D"
return "F"
Before you run it, say out loud what it will print. Then open the answer.
What it prints
>>>
2
Test exactly at the boundary. 60 should be D but the bug returns F, so this fails.
assert grade(60) == "D"
Before you run it, say out loud what it will print. Then open the answer.
What it prints
Traceback (most recent call last):
File "main.py", line 1, in
AssertionError
Before you run it, say out loud what it will print. Then open the answer.
What it prints
>>>
4
The value just below still behaves as intended, confirming the boundary is now correct.
assert grade(59) == "F"
Before you run it, say out loud what it will print. Then open the answer.
What it prints
>>>
Put it in order
Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.
Data Flow
You pick the values where a rule changes (empty, boundary, awkward), assert those, and the edge tests reveal bugs the middle cases miss.
1Find where the rule changes
↓
2Test below, at, above
↓
3Add empty and awkward values
↓
4Edge bugs surface
Why this worksTest the boundary
For any threshold, test just below, exactly at, and just above. That triple catches off-by-one errors that a middle value cannot. The rule is not test more random values, it is test the values where the rule changes.
Edges are where bugs cluster
Empty, single, zero, None, negative, and the largest value are where code forgets a branch. A handful of edge tests finds more bugs than a hundred random middle cases, because the middle cases all exercise the same happy path.
Examples
Empty and one
assert first([]) is None
assert first([7]) == 7
An empty list and a single item are edges. A function that works for three items often fails for zero or one. Test the counts at the boundary.
The boundary of a rule
assert grade(59) == "F"
assert grade(60) == "D"
If the rule says 60 is a D, test 59 and 60. A comparison written > instead of >= passes at 61 and fails exactly at 60. Off-by-one lives at the boundary.
The awkward value
assert count(None) == 0
None, empty string, and zero are values that many functions forget. A test that names them catches the forgetful branch before production does.
Words this lesson uses
Each one is defined here on this site. Open the entry for the full definition and the other words it points at.