Fix one bug, break another
You fix a bug in the cancel code. The cancel tests pass. You are happy.
Then somebody joins the waiting list and everything stops. Your fix changed something another part needed.
Good testing finds this early. There is more than one kind of test, and each finds a different kind of fault.
Key words
| Word | What it means | Example |
|---|---|---|
| Module testing | Testing one part of the program on its own. | test next_in_line alone |
| Integration testing | Testing that parts work together as one system. | cancel, then promote |
| Automated testing | Tests that a program runs for you. | the Run Tests button |
| User testing | Watching real people use the solution. | a desk worker tries it |
| Regression testing | Running old tests again after a change, to check nothing broke. | run all tests after a fix |
| Function | A named piece of code that does one job. | cancel() |
| Promote | Move a person from the waiting list into a booking. | Aisha is promoted when Sam cancels |
| Bug report | A clear note about a fault: what, where, how to see it. | Promote leaves a name in the list |
| Change log | A list of what changed, when and why. | v3: fixed promote |
| Test data | The values you type in to test. Some are good and some are bad on purpose. | hour 12, hour 21 |
The cells students fill in are left blank in this preview.
Four kinds of test on the waiting list
| Kind | Hillside example | Who or what runs it |
|---|---|---|
| Module | next_in_line gives the first name for a slot | The developer |
| Integration | A cancel gives the court to the first person waiting | The developer |
| Automated | All the tests run when you press one button | A program |
| User | A desk worker tries to join Sam to the list | A real person |
The cells students fill in are left blank in this preview.
Try it: a tiny automated test
def next_in_line(waiting, court, hour):
for one in waiting:
if one["court"] == court and one["hour"] == hour:
return one["member"]
return None
def test_first_name():
waiting = [{"member": "Sam", "court": 1, "hour": 10}]
assert next_in_line(waiting, 1, 10) == "Sam"
def test_nobody():
assert next_in_line([], 1, 10) is None
for test in (test_first_name, test_nobody):
test()
print(test.__name__, "passed")
Quick check: which test?
Have a go
Quick check: which test?
5 questionsYour answers are marked here and are not kept. A college account keeps every student’s marks.
Five kinds of test data
| Kind | Hillside example (hours 9 to 20 are open) | Expected |
|---|---|---|
| Valid | Hour 12 | Booked |
| Valid extreme (the edge that still works) | Hours 9 and 20 | Booked |
| Invalid | Hour 30 | Refused |
| Invalid extreme (just past the edge) | Hours 8 and 21 | Refused |
| Erroneous (the wrong kind of data) | The word "ten" | Refused with a clear message |
The cells students fill in are left blank in this preview.
Try it: run the five kinds of data
def book(bookings, member, court, hour):
if hour < 9 or hour > 20 or court not in (1, 2):
return False
bookings.append({"member": member, "court": court, "hour": hour})
return True
tests = [("normal", 12), ("lowest valid", 9), ("highest valid", 20),
("too low", 8), ("too high", 21), ("wrong type", "ten")]
for kind, hour in tests:
try:
print(kind, hour, "->", book([], "Sam", 1, hour))
except TypeError:
print(kind, hour, "-> CRASH: text cannot be compared with a number")
# The last test finds a fault. How would you fix it?