Easier to read with cream paper, a tint, more contrast or larger text? Choose your colours and text, once, for every page.

Free lesson

Test-driven development: red, green, refactor

After this lesson: I can write a failing test first, make it pass and tidy up.

60 min lesson12 commandsEveryday Linux commands lab: the thirty commands engineers type from memory

Test-driven development writes the test before the code

Test-driven development, or TDD, is a way of working. You write a failing test first, then just enough code to make it pass, then tidy up. You repeat that in small steps.

A team that works this way always has a test for the line it just wrote. It also writes only the code that something asks for.

By the end of this lesson

Note

I can write a failing test first, make it pass and tidy up.

Key words

WordWhat it meansExample
Test-driven developmentwriting the test before the code it checksTDD
Redthe step where a new test fails
Greenthe step where all tests pass
Refactorto tidy the code without changing what it does
Stubthe smallest code that lets a test run and fail

The cells students fill in are left blank in this preview.

The cycle is red, green, refactor

Red: write one small test for a behaviour that does not exist yet, and watch it fail. Green: write the least code that makes every test pass. Refactor: tidy the code, and run the tests again to see that nothing broke.

Then pick the next small behaviour and go round again.

Why watch a test fail first

A test that has never failed might be unable to fail. Maybe it checks nothing. By running it before the code exists, you see it go red for the right reason.

You also meet the design from the outside first: how a caller will use the function, before you decide how it works inside.

Make a folder for this lesson

Command
mkdir -p ~/tddtest

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Go into it

Command
cd ~/tddtest

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Red: write the first test, before any code

Command
printf 'import unittest\nfrom people import initials\n\n\nclass InitialsTest(unittest.TestCase):\n    def test_two_names(self):\n        self.assertEqual(initials("Ada Lovelace"), "AL")\n' > test_people.py

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run it: nothing exists yet

Command
python3 -m unittest test_people

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

The first red is an error

The Run ended with ModuleNotFoundError: No module named 'people', and FAILED (errors=1). There is no people.py yet, so the test cannot even load.

Write a stub: a function that exists and gives a wrong answer. Now the test runs, and fails because the answer is wrong.

Write a stub that returns nothing useful

Command
printf 'def initials(name):\n    return ""\n' > people.py

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run it: red for the right reason

Command
python3 -m unittest test_people

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Green: write the least code that passes

Command
printf 'def initials(name):\n    parts = name.split()\n    return parts[0][0] + parts[-1][0]\n' > people.py

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run it: green

Command
python3 -m unittest test_people

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

The next test finds the gap

The code takes the first and last words. A person with three names, such as Mary Jane Watson, would give the wrong answer. Do not guess. Write the test, and let it fail.

Red again: add a test for three names

Command
printf '    def test_three_names(self):\n        self.assertEqual(initials("Mary Jane Watson"), "MJW")\n' >> test_people.py

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run it: one test fails

Command
python3 -m unittest test_people

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Green and tidy: a loop over every name

Command
printf 'def initials(name):\n    letters = ""\n    for part in name.split():\n        letters = letters + part[0]\n    return letters\n' > people.py

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

Run it: both tests pass

Command
python3 -m unittest test_people

Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.

A common mistake

Warning

Many people write the whole function first and the tests afterwards, or write many tests at once. You then do not know which step made a test pass. Add one test, see it fail, write only the code for it, and see it pass. The steps stay small.

About this lab and its source: Everyday Linux commands lab: the thirty commands engineers type from memory

Teaching a class? Set this module for your students with a teacher guide, marked assignments and a lab for every student.