TDD and regression tests · Software Testing
When is the best moment to write a test?
Course contentsWhy you need it
Writing the test first sounds backwards, but it's one of the most useful habits in programming. It forces you to decide what the code should do before you write it, proves the test can actually fail, and leaves a guard behind that stops the same bug from ever coming back.
The intuition
Test-driven development (TDD) is a short loop:
- Red: write a test for the next small behaviour. Run it: it fails (the code doesn't do it yet).
- Green: write the simplest code that makes it pass.
- Refactor: tidy the code while all tests stay green.
Fixing a bug works the same way. Before touching the code, write a test that reproduces the bug report. Watch it fail: now you know you've understood the bug. Fix the code, watch it pass. Keep the test forever: it's a regression test, and if anyone brings the bug back, it fails.
Read a failing test from the bottom: what was expected, what came back, on which input. That's usually enough to find the cause.
Definition
TDD
Test-driven development: write a failing test, make it pass, then tidy up, in small steps.
Regression test
A test written for a bug that was fixed, kept so the bug can't silently return.
Examples
Reproduce, then fix
Try it yourselfBug report: word_count(" hello world ") returns 7. The test fails first, then passes after the fix.
def word_count_old(text):
return len(text.split(" "))
def word_count_new(text):
return len(text.split())
def test_extra_spaces(word_count):
got = word_count(" hello world ")
assert got == 2, f"expected 2, got {got}"
for version in [word_count_old, word_count_new]:
try:
test_extra_spaces(version)
print(version.__name__, "PASS")
except AssertionError as e:
print(version.__name__, "FAIL:", e)word_count_old FAIL: expected 2, got 7 word_count_new PASS
- L2split(" ") makes an empty string for every extra space.
- L5split() with no argument skips runs of spaces.
- L7The regression test, written from the bug report.
- L11Red with the old code, green with the new.
Red, green, refactor
Try it yourselfThree tiny TDD rounds for FizzBuzz, each driven by one new test.
def fizzbuzz(n):
if n % 15 == 0:
return "FizzBuzz"
if n % 3 == 0:
return "Fizz"
if n % 5 == 0:
return "Buzz"
return str(n)
TESTS = [(1, "1"), (3, "Fizz"), (5, "Buzz"), (15, "FizzBuzz"), (30, "FizzBuzz")]
for n, want in TESTS:
assert fizzbuzz(n) == want, (n, fizzbuzz(n))
print("all", len(TESTS), "tests green")all 5 tests green
- L8Round 1: the test for 1 → "1" only needed this line.
- L5Round 2: the test for 3 added this branch.
- L3Round 3: 15 failed with "Fizz", so this check had to come first.
Common mistakes
Fixing before reproducing
If you never saw the test fail, you don't know whether it tests the bug at all. Red first.
Changing the test to make it pass
When a test fails, ask which is wrong: the code or the test. Only change the expected value if the requirement really says so.
Giant steps
Writing ten tests and then all the code loses the point. One small test, make it pass, repeat.
Quick quiz
1. A bug is reported. What should you do first?
Show the answer
Answer: A
A failing test proves you understand the bug, then proves the fix works, then guards against it forever.
2. In TDD, what comes right after writing a failing test?
Show the answer
Answer: A
Red → green → refactor, in small steps.
Practice
3 exercises in three steps: predict what a program prints, change a program to meet a new requirement, then write one from scratch. Your code runs in the browser and is checked the moment you submit.
- PredictWhich tests fail?
Type exactly what this prints.
- ModifyFix bug #42
The regression test for bug #42 is already written and fails. Fix
parse_priceso it passes, and also handle spaces around the text (" $12,000 "→12000.0). Don't change the test. - WriteMake the tests green
The tests came first. Write
to_roman(n)for 1 to 3999 so they all pass:1 → I 4 → IV 9 → IX 14 → XIV 40 → XL 90 → XC 400 → CD 1994 → MCMXCIV 2026 → MMXXVI 3999 → MMMCMXCIX
Practise this lesson
Open the interactive lesson to edit and run these examples and solve the exercises with instant feedback. Free to start, no sign-up.
Interactive lessons open in order. If you have not done the earlier ones yet, you start at the course map.
Terms in this lesson
| Term | Chinese | Definition |
|---|---|---|
| TDD | 测试驱动开发 | Red, green, refactor: write the test first. |
| Regression test | 回归测试 | A test kept so a fixed bug can't come back. |