Mocking replaces a real dependency with a fake stand-in object that records how it was called and returns whatever you tell it to. You use it to isolate the code under test from slow, unreliable, or side-effecting collaborators — network calls, databases, the clock, third-party APIs.
from unittest.mock import Mock
service = Mock()
service.fetch.return_value = {"id": 1} # canned response
result = service.fetch("/users/1") # no real network call
result # {"id": 1}
service.fetch.assert_called_once() # verify it happened
Mocking makes tests fast, deterministic, and focused on your logic rather than the dependency's. Rule of thumb: mock at the boundaries of your system (I/O, external services), not your own pure functions.
Both auto-create attributes and methods on access. The difference: MagicMock
additionally supports magic (dunder) methods — __len__, __iter__,
__getitem__, __enter__/__exit__, etc. — so it can stand in for objects used
with len(), iteration, indexing, or with. A plain Mock raises on dunder
access.
from unittest.mock import Mock, MagicMock
m = Mock()
len(m) # TypeError — Mock has no __len__
mm = MagicMock()
mm.__len__.return_value = 3
len(mm) # 3 — magic methods supported
list(mm) # works — __iter__ is mocked too
patch() uses MagicMock by default, which is why patched objects "just work" in
most cases. Use MagicMock when the dependency relies on protocols/dunders; Mock
is fine for plain method calls.
patch temporarily replaces an object with a mock for the duration of a test,
as a decorator or a context manager, restoring the original afterward. The
critical rule is "patch where it's looked up, not where it's defined" — you patch
the name in the module that imports and uses it.
# app.py
from time import time
def stamp(): return time()
# test.py — patch the reference INSIDE app, not 'time.time'
from unittest.mock import patch
@patch("app.time") # where it's USED
def test_stamp(mock_time):
mock_time.return_value = 123
assert stamp() == 123
with patch("app.time") as mock_time: # context-manager form
mock_time.return_value = 123
Patching "time.time" here would fail, because app already bound its own time
name at import. Always target the importing module's namespace — this is the
single most common mocking mistake.
return_value sets a single fixed value the mock returns on every call.
side_effect is more powerful: assign a function (called with the same args),
an exception (which gets raised), or an iterable (returning a different value
per successive call).
from unittest.mock import Mock
m = Mock(return_value=42)
m(); m() # 42, 42 — always the same
m.side_effect = [1, 2, 3] # one per call
m(); m() # 1, then 2
m.side_effect = ValueError("boom")
m() # raises ValueError
m.side_effect = lambda x: x * 2
m(10) # 20 — computed from the arg
Use return_value for a constant stub, and side_effect to raise errors,
vary results across calls, or compute based on arguments. If both are set,
side_effect wins (unless it returns the sentinel DEFAULT).
Mocks record every call, so you verify interactions with the assert_called*
family. Check whether/how many times it was called and with what arguments,
and inspect history via call_args / call_args_list.
from unittest.mock import Mock, call
m = Mock()
m(1, 2)
m(3, key="v")
m.assert_called() # at least once
m.assert_called_once() # exactly once -> would FAIL here
m.assert_called_with(3, key="v") # the MOST RECENT call
m.assert_any_call(1, 2) # any call matched
m.assert_has_calls([call(1, 2), call(3, key="v")])
m.call_count # 2
Note assert_called_with checks only the last call — use assert_any_call or
assert_has_calls for earlier ones. Beware typos: a misspelled assertion (e.g.
assert_called_once_with -> assert_called_onced_with) silently passes, so spell
these carefully.
A normal mock accepts any attribute access or call signature, so it can hide bugs
— a test passes even if you call a method that doesn't exist or with wrong arguments.
Autospec (autospec=True, or create_autospec) builds the mock to match the
real object's API, so it rejects nonexistent attributes and mismatched signatures.
from unittest.mock import patch, create_autospec
class Api:
def fetch(self, url): ...
@patch("app.Api", autospec=True)
def test_it(MockApi):
api = MockApi()
api.fetch("/x") # OK — matches real signature
api.fetch() # TypeError — missing 'url'!
api.delete() # AttributeError — no such method
Autospec makes mocks stay in sync with the real interface, catching drift when the real API changes. The tradeoff is a small overhead, but it's strongly recommended for non-trivial dependencies.
patch.object(target, "attr") patches an attribute on an object/class you
already have a reference to — clearer and refactor-safe (no string path to a name).
patch("module.attr") uses a string path; patch.object uses the actual object.
from unittest.mock import patch
from app import Service
with patch.object(Service, "fetch", return_value={"id": 1}) as m:
Service().fetch("/x") # patched method
m.assert_called_once()
# patch an instance attribute too:
svc = Service()
with patch.object(svc, "timeout", 5):
...
Rule of thumb: use patch.object when you hold the object/class directly (safer under
renames); use string-path patch when targeting a name in another module's namespace.
patch.dict temporarily modifies a dict (like os.environ or a config) for the
test and restores it afterward. Pass clear=True to start empty.
import os
from unittest.mock import patch
with patch.dict(os.environ, {"API_KEY": "test123"}):
assert os.environ["API_KEY"] == "test123"
# original environ restored here
with patch.dict("app.config", {"debug": True}, clear=True):
...
Rule of thumb: use patch.dict for env vars and config dicts — it guarantees cleanup
so tests don't leak state into each other.
spec=Cls restricts which attributes exist (but not call signatures).
spec_set additionally forbids setting new attributes. autospec goes
furthest — it also enforces method call signatures recursively.
from unittest.mock import Mock
m = Mock(spec=["fetch"]) # only .fetch allowed
m.fetch() # ok
m.missing # AttributeError
m2 = Mock(spec_set=SomeClass)
m2.new_attr = 1 # AttributeError — can't add attributes
Rule of thumb: spec to catch typo'd attributes, spec_set to also block stray
assignments, autospec for full signature checking — increasing strictness.
Properties need PropertyMock (assigned on the type, not the instance), since
accessing a property runs code. Plain attributes can be set directly on the mock.
from unittest.mock import patch, PropertyMock
class Account:
@property
def balance(self): ...
with patch.object(Account, "balance", new_callable=PropertyMock) as m:
m.return_value = 100
assert Account().balance == 100 # property access returns 100
Rule of thumb: use PropertyMock via new_callable to fake a property; for ordinary
attributes, just assign mock.attr = value.
with calls __enter__, whose return value is bound by as. With MagicMock,
set mock.__enter__.return_value (or mock.return_value.__enter__.return_value
when patching the class) to the object the block should receive.
from unittest.mock import patch, MagicMock
with patch("app.open") as mock_open:
handle = mock_open.return_value.__enter__.return_value
handle.read.return_value = "data"
# code doing `with open(...) as f: f.read()` now gets "data"
Rule of thumb: for with targets, configure __enter__.return_value; or use the
built-in helper unittest.mock.mock_open for file-open patterns.
Excessive mocking couples tests to implementation details (which methods are called, in what order), making them brittle and giving false confidence — they can pass while the real integration is broken. Mocks can also drift from the real API.
# over-mocked: tests internal call sequence, not behavior — brittle
mock_db.query.assert_called_with("SELECT ...")
# better: test observable behavior against a real/fake in-memory dependency
result = service.get_user(1)
assert result.name == "Ada"
Rule of thumb: mock only true external boundaries (network, time, filesystem); for internal collaborators prefer real objects or fakes, and assert on outcomes not call mechanics.
unittest.mock.mock_open is a prebuilt helper that mocks open() with working
read, readline, iteration, and context-manager support — so you can test
file-reading code without touching the disk.
from unittest.mock import patch, mock_open
m = mock_open(read_data="line1\nline2")
with patch("app.open", m):
# code doing `with open(path) as f: f.read()` sees "line1\nline2"
...
m.assert_called_once_with("path/to/file")
Rule of thumb: use mock_open(read_data=...) for file-read tests instead of
hand-building __enter__/read mocks; patch open in the module that uses it.
mock.reset_mock() clears recorded calls (call_count, call_args_list) without
removing configured return_value/side_effect. Useful to reuse one mock across
phases of a test. Pass return_value=True/side_effect=True to also clear those.
from unittest.mock import Mock
m = Mock(return_value=1)
m(); m()
m.call_count # 2
m.reset_mock()
m.call_count # 0
m.return_value # still 1 — config preserved
Rule of thumb: reset_mock() to clear call history mid-test while keeping stubs;
generally prefer a fresh mock per test for clarity.
Use AsyncMock (Python 3.8+), whose calls return awaitables. patch auto-uses it
when the target is an async def. Configure return_value/side_effect as usual;
the result is awaited.
from unittest.mock import AsyncMock, patch
async def test_it():
m = AsyncMock(return_value={"id": 1})
assert await m("/x") == {"id": 1}
m.assert_awaited_once_with("/x")
@patch("app.fetch", new_callable=AsyncMock) # explicit if needed
async def test_fetch(mock_fetch): ...
Rule of thumb: mock coroutines with AsyncMock and assert via assert_awaited*;
patch detects async targets automatically in modern Python.
More Testing interview questions
More ways to practice
The self-quiz is live. Join our channel for updates, new content & tech tips.