Mocking with unittest.mock
Replace slow, random or external parts of a program with test doubles using Mock, patch and autospec, and check how your code called them.
What you will learn
- Create a Mock, set return values and side effects, and inspect the calls it received
- Replace a name with patch, as a decorator and as a context manager
- Say where to patch, and why the module that uses a name is the one to patch
- Use autospec so a mock rejects typos and wrong signatures
On this page
Some code cannot be tested by calling it and comparing the answer. It asks a web service for the temperature, reads the clock, rolls dice, or sends an email. In a test you want none of that: no network, a clock that stands still, and no real mail. The tool is a test double, an object that stands in for the real one. Python’s standard library has a complete kit in unittest.mock, and it works the same under unittest and pytest.
Mock: an object that accepts anything and remembers it
A Mock allows any attribute access and any call. It records what happened, so afterwards you can ask.
from unittest.mock import Mock, call
# A Mock accepts any call and remembers it.sender = Mock()sender.send("ana@example.com", "Hello")sender.send("ben@example.com", "Hi")
print(sender.send.call_count)print(sender.send.call_args)print(sender.send.call_args_list == [ call("ana@example.com", "Hello"), call("ben@example.com", "Hi"),])sender.send.assert_any_call("ben@example.com", "Hi")2call('ben@example.com', 'Hi')Truecall_count is the number of calls, call_args the last one, and call_args_list all of them. sender.send was created the moment you used it. assert_any_call raises AssertionError if no call matched, so as a line in a test it needs no print. The other assert methods are assert_called_once(), assert_called_once_with(...), assert_called_with(...) (last call only) and assert_not_called().
The second half of the same script controls what the mock gives back. return_value is a fixed answer, and side_effect is more flexible: give it a list for one answer per call, or an exception to raise.
from unittest.mock import Mock
sender = Mock()sender.send("ana@example.com", "Hello")sender.send("ben@example.com", "Hi")
lookup = Mock(return_value=42)print(lookup("anything"))
dice = Mock(side_effect=[3, 5, 1])print(dice(), dice(), dice())
broken = Mock(side_effect=ConnectionError("network down"))try: broken()except ConnectionError as error: print("raised:", error)
try: sender.send.assert_called_once()except AssertionError as error: print("assertion:", str(error).splitlines()[0])423 5 1raised: network downassertion: Expected 'send' to have been called once. Called 2 times.The two send calls at the top repeat the set-up from the first script. side_effect with an exception is how you test the unhappy path, such as what your code does when the network is down, without needing a broken network. When a list runs out, the next call raises StopIteration.
patch: swap a name for the duration of a test
Mocks are most useful when they replace something that real code calls. patch("module.name") replaces name in module with a MagicMock (a Mock that also supports operators like len() and in) and restores the original when it exits. Here is code that depends on the network:
"""Code that talks to the network, split so the network part can be replaced."""
import jsonfrom urllib.request import urlopen
def fetch_temperature(city): url = f"https://weather.example.com/api?city={city}" with urlopen(url, timeout=5) as response: return json.load(response)["celsius"]
def describe(city): celsius = fetch_temperature(city) if celsius < 5: return f"{city}: {celsius} C, wear a coat" return f"{city}: {celsius} C, no coat needed"describe holds the logic worth testing: the threshold and the wording. fetch_temperature is the part that needs the network. Patch that one:
The first test uses patch as a decorator:
import unittestfrom unittest.mock import patch
import c36_weather
FETCH = "c36_weather.fetch_temperature"
class DescribeTests(unittest.TestCase): @patch(FETCH) def test_cold_day(self, fake_fetch): fake_fetch.return_value = 2 result = c36_weather.describe("Oslo") self.assertEqual(result, "Oslo: 2 C, wear a coat") fake_fetch.assert_called_once_with("Oslo")The other two use it as a context manager:
def test_warm_day(self): with patch(FETCH, return_value=21) as fake_fetch: result = c36_weather.describe("Lima") self.assertEqual(result, "Lima: 21 C, no coat needed") self.assertEqual(fake_fetch.call_count, 1)
def test_network_failure_reaches_the_caller(self): with patch(FETCH, side_effect=ConnectionError): with self.assertRaises(ConnectionError): c36_weather.describe("Oslo")
if __name__ == "__main__": unittest.main()$ python -m unittest -v c36_patch_basic.pytest_cold_day (c36_patch_basic.DescribeTests.test_cold_day) ... oktest_network_failure_reaches_the_caller (c36_patch_basic.DescribeTests.test_network_failure_reaches_the_caller) ... oktest_warm_day (c36_patch_basic.DescribeTests.test_warm_day) ... ok
----------------------------------------------------------------------Ran 3 tests in 0.001s
OKpatch appears in two forms. As a decorator (test_cold_day), the fake is passed in as an extra argument. As a context manager (test_warm_day), the patch lasts only for the with block, and arguments such as return_value=21 configure the mock directly. Both undo themselves, even if the test fails. No test needed the internet, and all three finish in a millisecond.
Under pytest, the same patches work with with patch(...) inside a plain test function. For a patch that should last a whole test, pytest’s monkeypatch.setattr from the previous lesson does the same job without the unittest.mock machinery. Reach for patch when you also want to inspect the calls.
Patch where the name is looked up
The most common mocking mistake is patching the right function in the wrong place. Consider two small modules:
from datetime import datetime
def current_hour(): return datetime.now().hourThis naive datetime.now() is fine for a demonstration. For real timestamps, prefer an aware datetime.now(tz=UTC), as explained in the dates and times lesson.
from c36_clock import current_hour
def banner(): return "Good morning" if current_hour() < 12 else "Good afternoon"c36_shop did from c36_clock import current_hour. That statement copied the function object into the shop module under its own name. Patching c36_clock.current_hour replaces the name in the clock module and leaves the shop’s copy alone:
from unittest.mock import patch
import c36_shop
# c36_shop did `from c36_clock import current_hour`, so the shop module# holds its own name for the function. Patching the clock module replaces# a different name.with patch("c36_clock.current_hour", return_value=9): print("shop sees:", type(c36_shop.current_hour).__name__)
# Patch the name where it is looked up: in the shop module.with patch("c36_shop.current_hour", return_value=9): print("shop sees:", type(c36_shop.current_hour).__name__) print(c36_shop.banner())
with patch("c36_shop.current_hour", return_value=15): print(c36_shop.banner())shop sees: functionshop sees: MagicMockGood morningGood afternoonThe rule: patch the name in the module that uses it, not the module that defines it. If c36_shop had written import c36_clock and called c36_clock.current_hour(), then patching c36_clock.current_hour would work, because the lookup happens through the clock module at call time. The modules and imports lesson explains why the two import forms behave differently.
autospec: mocks that know the real signature
A plain Mock accepts any attribute, so a typo in your code under test can pass unnoticed. A mock created from the real class refuses what the real object would refuse.
from unittest.mock import Mock, create_autospec
class Mailer: def send(self, to, subject): raise RuntimeError("would really send mail")
# A plain Mock accepts any attribute, so a typo goes unnoticed.loose = Mock()loose.sned("ana@example.com", "Hi")print("typo accepted:", loose.sned.called)Now the same call against a mock built from the real class, which also checks signatures:
# A spec'd mock only has the attributes of the real class.strict = create_autospec(Mailer, instance=True)strict.send("ana@example.com", "Hi")print("send called:", strict.send.called)
try: strict.sned("ana@example.com", "Hi")except AttributeError as error: print("AttributeError:", error)
# autospec also checks the signature of each call.try: strict.send("ana@example.com")except TypeError as error: print("TypeError:", error)typo accepted: Truesend called: TrueAttributeError: Mock object has no attribute 'sned'TypeError: missing a required argument: 'subject'patch("module.name", autospec=True) gives the same protection to a patched name. Prefer it whenever the real thing exists, because a test using a loose mock can keep passing after the real function’s signature changes.
What not to mock
Mocking has a cost: the test now checks that your code calls the mock the way you believe the real thing works. If you are wrong, the test still passes. Keep mocks for the edges of your program, such as the network, the clock, randomness, and the file system when real files are awkward. Do not mock your own pure functions: call them for real. A test that mocks every collaborator checks wiring and nothing else. For files, the tmp_path fixture is usually better than mocking open.
Common pitfalls
- Patching in the wrong module. If the mock seems ignored, read the section above again and patch where the name is used.
- Forgetting that
Mockinvents attributes.mock.sned(...)is a valid call on a plain mock, so a misspelt method in your own code goes unnoticed. Useautospecfor mocks of real classes and functions. - Decorator argument order. With several
@patchdecorators, the mocks are passed bottom-up: the decorator closest to the function gives the first argument. - Asserting on the mock instead of the behaviour. If the code returns a value, assert the value. Check calls only when the call is the behaviour, such as “an email was sent”.
- Leaving a patch running. Use the decorator or
withforms, which clean up. Apatcher.start()withoutstop()leaks into later tests. - Mocking what you could simply pass in. If
fetch_with_retry(fetch)takes the function as a parameter, a test can hand it a plainMock, with nopatchat all. Designing code that way is often the simplest fix.
Work out the seven lines this script prints before running it. Pay attention to reset_mock().
from unittest.mock import Mock
clock = Mock(side_effect=[10, 20, ValueError("no more ticks")])
print(clock())print(clock())try: clock()except ValueError as error: print("error:", error)
clock.reset_mock()print(clock.call_count)
greeter = Mock(return_value="hi")print(greeter("Ana"), greeter("Ben"))print(greeter.call_args_list)print(greeter.call_args.args)Show solution
1020error: no more ticks0hi hi[call('Ana'), call('Ben')]('Ben',)When an item of a side_effect list is an exception, the mock raises it. reset_mock() clears the recorded calls, so call_count goes back to 0. It does not clear the configured side_effect or return_value unless you ask. call_args is the last call, and its .args is the tuple of positional arguments.
Write tests for this function, saved as c36_retry.py, with unittest.mock. A real run would sleep for seconds, so patch time.sleep. Check three things. First, a fetch that fails twice and then returns 7 gives 7 after three calls, with sleeps of 1 and 2 seconds. Second, a fetch that always fails with ConnectionError is called twice when attempts=2, the error reaches the caller, and sleep was called once with 1. Third, a fetch that works straight away never sleeps. Run them: the report should end with Ran 3 tests and OK.
import time
def fetch_with_retry(fetch, attempts=3): """Call fetch() until it works, at most `attempts` times.""" for attempt in range(1, attempts + 1): try: return fetch() except ConnectionError: if attempt == attempts: raise time.sleep(attempt)Show solution
import unittestfrom unittest.mock import Mock, patch
from c36_retry import fetch_with_retry
class RetryTests(unittest.TestCase): @patch("time.sleep") def test_succeeds_on_the_third_try(self, fake_sleep): fetch = Mock(side_effect=[ConnectionError, ConnectionError, 7]) self.assertEqual(fetch_with_retry(fetch), 7) self.assertEqual(fetch.call_count, 3) sleeps = [c.args for c in fake_sleep.call_args_list] self.assertEqual(sleeps, [(1,), (2,)])The other two tests follow the same pattern:
@patch("time.sleep") def test_gives_up_after_the_last_attempt(self, fake_sleep): fetch = Mock(side_effect=ConnectionError("down")) with self.assertRaises(ConnectionError): fetch_with_retry(fetch, attempts=2) self.assertEqual(fetch.call_count, 2) fake_sleep.assert_called_once_with(1)
@patch("time.sleep") def test_no_pause_when_the_first_call_works(self, fake_sleep): fetch = Mock(return_value="ok") self.assertEqual(fetch_with_retry(fetch), "ok") fake_sleep.assert_not_called()
if __name__ == "__main__": unittest.main()$ python -m unittest -v c36_ex_retry.pytest_gives_up_after_the_last_attempt (c36_ex_retry.RetryTests.test_gives_up_after_the_last_attempt) ... oktest_no_pause_when_the_first_call_works (c36_ex_retry.RetryTests.test_no_pause_when_the_first_call_works) ... oktest_succeeds_on_the_third_try (c36_ex_retry.RetryTests.test_succeeds_on_the_third_try) ... ok
----------------------------------------------------------------------Ran 3 tests in 0.001s
OKThe function takes fetch as a parameter, so a Mock is passed in directly. Only time.sleep is patched. The module calls time.sleep through the time module at run time, which makes "time.sleep" the right place to patch.
This test patches the clock, but the run ends with FAILED (failures=1) and 0 != 1: the mock was never called. Using the section on where to patch, change one line so the run ends with OK.
import unittestfrom unittest.mock import patch
import c36_shop
class BannerTests(unittest.TestCase): def test_morning_banner(self): with patch("c36_clock.current_hour", return_value=9) as fake_hour: c36_shop.banner() self.assertEqual(fake_hour.call_count, 1)
if __name__ == "__main__": unittest.main()Show solution
c36_shop copied current_hour into its own namespace with from c36_clock import current_hour. Patching c36_clock.current_hour replaces a name the shop never looks at. Patch the name where it is used:
import unittestfrom unittest.mock import patch
import c36_shop
class BannerTests(unittest.TestCase): def test_morning_banner(self): with patch("c36_shop.current_hour", return_value=9) as fake_hour: result = c36_shop.banner() self.assertEqual(fake_hour.call_count, 1) self.assertEqual(result, "Good morning")
if __name__ == "__main__": unittest.main()$ python -m unittest -v c36_ex_wrong_target_fixed.pytest_morning_banner (c36_ex_wrong_target_fixed.BannerTests.test_morning_banner) ... ok
----------------------------------------------------------------------Ran 1 test in 0.001s
OKThe patch now takes effect, so the fake is called once and the banner says Good morning whatever the real time is. The new assertion on the result would have been flaky before the fix.
Summary
- A
Mockaccepts any call and records it.return_valueandside_effect(a list, or an exception) decide what it gives back, andcall_args_listand theassert_called_*methods check how it was used. patch("module.name")swaps a name for a mock as a decorator or awithblock and restores it afterwards. Patch the name in the module that looks it up.- Use
autospecso a mock matches the real object’s attributes and signature. Mock only the edges of your program, and pass collaborators as arguments when you can. - The next lesson turns the examples in documentation into tests.