Python mocking & patching, explained
Good unit tests isolate the code under test from slow or external things — networks,
databases, the clock. Mocking replaces those dependencies with stand-ins you control and
inspect. Python's unittest.mock does this, and the one rule that trips everyone up is
where to patch.
Mock objects record everything
A Mock is a flexible object that auto-creates attributes and methods on access, returns
another Mock when called, and records how it was used. MagicMock adds support for dunder
methods (so it works with len, [], with, etc.).
from unittest.mock import Mock
service = Mock()
service.fetch(42) # returns a Mock, records the call
service.fetch.assert_called_once_with(42) # verify it happened
service.fetch.call_count # 1
You don't define anything up front — the Mock fabricates attributes on demand and remembers every interaction for later assertions.
Setting return values and side effects
Control what a mock returns with return_value, or use side_effect for dynamic behaviour,
raising exceptions, or returning different values on successive calls.
from unittest.mock import Mock
m = Mock()
m.get.return_value = {"id": 1} # always returns this
m.get() # {'id': 1}
m.fetch.side_effect = ValueError("boom") # raises when called
m.next.side_effect = [1, 2, 3] # returns 1, then 2, then 3
side_effect can also be a function, letting the mock compute a result from its arguments.
patch replaces a name temporarily
patch swaps a real object for a mock for the duration of a test, then restores it. Use it as
a decorator or context manager.
from unittest.mock import patch
# code under test: app.py calls requests.get(...)
def get_user(uid):
import requests
return requests.get(f"/users/{uid}").json()
@patch("app.requests") # replace requests inside app
def test_get_user(mock_requests):
mock_requests.get.return_value.json.return_value = {"id": 1}
assert get_user(1) == {"id": 1}
mock_requests.get.assert_called_once_with("/users/1")
The injected mock is passed as an argument (decorator) so you can configure and assert on it.
The golden rule: patch where it's used
This is the #1 mocking mistake. You must patch the name in the module where it's looked
up, not where it's defined. If app.py does from utils import fetch, patch
app.fetch, not utils.fetch.
# utils.py: def fetch(): ...
# app.py: from utils import fetch → creates app.fetch
@patch("utils.fetch") # WRONG — app already bound its own reference
@patch("app.fetch") # RIGHT — patch the name app actually calls
def test_it(mock_fetch): ...
Because from x import y copies the reference into the importing module, patching the origin
has no effect on the already-bound name.
Asserting how a mock was called
Mocks accumulate a rich call history you can assert against — that the call happened, with what arguments, how many times, and in what order.
m.method.assert_called() # called at least once
m.method.assert_called_once() # exactly once
m.method.assert_called_with(1, key="v") # most recent call's args
m.method.assert_not_called()
m.method.call_args # call(1, key='v')
m.method.call_args_list # every call, in order
These turn "did my code interact with the dependency correctly?" into precise checks.
patch.object and autospec
patch.object patches a single attribute of an object (cleaner than a string path), and
autospec=True makes the mock match the real signature so bad calls fail the test instead of
silently passing.
from unittest.mock import patch
with patch.object(MyClass, "method", return_value=5):
...
@patch("app.client", autospec=True) # enforces the real interface
def test_safe(mock_client): ...
autospec catches the dangerous case where a mock happily accepts a call that the real object
would reject.
Recap
unittest.mock isolates code by replacing dependencies with Mock/MagicMock objects that
fabricate attributes and record every call. Drive behaviour with return_value and
side_effect (values, exceptions, or a function), swap real names with patch (decorator
or context manager), and verify interactions with assert_called_*, call_args, and
call_args_list. The crucial rule: patch where the name is used, not where it's defined.
Use patch.object for a single attribute and autospec=True to keep mocks honest about
signatures.