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 doubles: fakes, stubs and mocks

After this lesson: I can test code that depends on the time or on chance by replacing it with a fixed fake.

55 min lesson7 commandsEveryday Linux commands lab: the thirty commands engineers type from memory

A test double replaces a clock

A test double, also called a fake, is a stand-in that your test hands to the code. It replaces something slow, random or outside your control. With one, a test that depends on the time of day gives the same result at nine in the morning and at midnight.

A greeting that says "Good morning" before noon cannot be tested honestly after noon. A fake clock fixes that.

By the end of this lesson

Note

I can test code that depends on the time or on chance by replacing it with a fixed fake.

Key words

WordWhat it meansExample
Dependencysomething your code needs from outsidethe clock
Test doublea stand-in used in place of a dependency
Fakea simple working stand-in with a fixed answer
Mocka stand-in that also records how it was called
Flaky testa test that sometimes passes and sometimes fails

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

Real clocks make flaky tests

Here is a function that greets a user. It asks the real clock for the hour. A test of it passes in the morning and fails in the afternoon, with no change to the code. That is a flaky test, and it teaches a team to ignore red results.

The time is a dependency: something the code needs from outside, and the test cannot control.

Make a folder for this lesson

Command
mkdir -p ~/faketest

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 ~/faketest

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

Write a greeting that can take a clock

Command
printf 'import datetime\n\n\ndef real_hour():\n    return datetime.datetime.now().hour\n\n\ndef greeting(get_hour=real_hour):\n    if get_hour() < 12:\n        return "Good morning"\n    return "Good afternoon"\n' > greet.py

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

Ask for a greeting with the real clock

Command
python3 -c "from greet import greeting; print(greeting())"

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

Pass the clock in as a function

The Run printed Good morning or Good afternoon, depending on the hour where you are. The test cannot choose which.

Look at how greeting was written. It takes a function, get_hour, and the real clock is only the default. A test can pass a function of its own that always gives 9, or always 15.

Worked example: four fake hours

Fake hourExpected greetingWhat the case checks
9Good morningAn ordinary morning
11Good morningThe last hour before noon
12Good afternoonThe boundary: noon itself
15Good afternoonAn ordinary afternoon

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

Write tests with fake hours

Command
printf 'import unittest\nfrom greet import greeting\n\n\ndef nine_am():\n    return 9\n\n\ndef noon():\n    return 12\n\n\ndef three_pm():\n    return 15\n\n\nclass GreetingTest(unittest.TestCase):\n    def test_morning(self):\n        self.assertEqual(greeting(nine_am), "Good morning")\n\n    def test_noon_is_afternoon(self):\n        self.assertEqual(greeting(noon), "Good afternoon")\n\n    def test_afternoon(self):\n        self.assertEqual(greeting(three_pm), "Good afternoon")\n' > test_greet.py

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

Run the tests: they pass at any hour

Command
python3 -m unittest test_greet

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

Fakes, stubs and mocks compared

People use the words loosely, so here is a simple way to keep them apart.

A fake or stub gives a fixed answer: the nine_am function. A mock also records how it was called, so the test can ask "were you called once, with this value?"

Python's standard library has unittest.mock for this. You do not need to install anything.

Replace the real clock function with a mock

Command
python3 -c "import time; from unittest import mock; time.time = mock.Mock(return_value=1000); print(time.time())"

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

Chance is a dependency too

A prize draw that picks a random entry gives a different winner on every run. Do the same trick: let the draw take a function, pick_index. In the real program it picks at random. In a test it always picks the first, or the last, so the winner is known.

You test your own rule, and you leave the dice to Python.

A common mistake

Warning

Many people replace so much with fakes that the test checks only the fakes. If the fake and the real thing disagree, the test passes and the program fails. Fake only what is slow, random or outside your control, and keep one integration test with the real thing.

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.