Mock, MagicMock, patch
unittest.mock is the standard mocking library. Three things to keep straight: what to instantiate, how to install it, and where to install it.
Mock vs MagicMock
from unittest.mock import Mock, MagicMock
m = Mock()
m.foo() # OK — auto-creates `.foo`, returns another Mock
m() # OK — m is callable
len(m) # TypeError: object of type 'Mock' has no len()
m["key"] # TypeError
mm = MagicMock()
len(mm) # 0 — supports __len__
mm["key"] # MagicMock — supports __getitem__
mm + 1 # MagicMock — supports __add__
MagicMock pre-configures magic methods (dunders): __len__, __iter__, __getitem__, __contains__, __enter__, __exit__, etc. Use it for objects that need to behave like containers, context managers, or numerics.
Mock is leaner — use when you don’t need dunders.
return_value vs side_effect
m = Mock()
m.return_value = 42
m() # 42
m() # 42
return_value is what the mock returns when called.
side_effect is more flexible:
# Iterable: each call pops the next value
m.side_effect = [1, 2, 3]
m(); m(); m() # 1, 2, 3
m() # raises StopIteration
# Exception class or instance: raised
m.side_effect = ValueError("boom")
m() # raises ValueError
# Callable: invoked with the call args
m.side_effect = lambda x: x * 2
m(5) # 10
When both are set, side_effect wins, unless side_effect returns DEFAULT, in which case return_value is used.
patch — replace at a location
from unittest.mock import patch
@patch("myapp.api.requests.get")
def test_call(mock_get):
mock_get.return_value.json.return_value = {"ok": True}
result = myapp.api.fetch()
assert result == {"ok": True}
patch is a context manager / decorator that swaps an attribute on a module for the duration. Decorator argument order is reverse of decorator stacking:
@patch("myapp.b")
@patch("myapp.a")
def test(mock_a, mock_b): # innermost decorator → first arg
...
patch where it’s used, not where it’s defined
This is the #1 mock gotcha.
# myapp/api.py
import requests
def fetch():
return requests.get("https://...").json()
# Wrong: patches the requests module, but myapp.api already imported it
@patch("requests.get")
def test_fetch(mock_get): ...
# Right: patches the name as it lives in myapp.api
@patch("myapp.api.requests.get")
def test_fetch(mock_get): ...
from x import y binds y in the importing module’s namespace. Patching x.y changes the original; the importing module still references the old object.
patch.object
When you have the object in a variable, not a string path:
import myapp.api as api
with patch.object(api, "requests") as mock_requests:
mock_requests.get.return_value.json.return_value = {"ok": True}
api.fetch()
Equivalent to patch("myapp.api.requests") but the path is a Python expression rather than a string. Type checkers like it better.
patch.dict
For mutable mappings (notably os.environ):
@patch.dict("os.environ", {"API_KEY": "test-key"})
def test_api():
...
# Or as context manager:
with patch.dict(os.environ, {"API_KEY": "test"}, clear=True):
...
clear=True empties the dict before applying the patch. Restored on exit.
autospec — type-safety mocks
@patch("myapp.api.requests.get", autospec=True)
def test(mock_get):
mock_get(123, badarg="x") # TypeError: requests.get got unexpected kw 'badarg'
autospec=True inspects the real function’s signature. Calls with wrong arguments fail at test time, just like in production. Highly recommended.
For classes, autospec also restricts which attributes/methods exist:
@patch("myapp.client.HTTPClient", autospec=True)
def test(MockClient):
instance = MockClient.return_value
instance.connect() # OK
instance.nonexistent() # AttributeError
spec vs autospec
spec=SomeClass constrains the mock’s attributes to match the class. autospec=True also auto-spec’s signatures. spec_set= is spec with attribute setting also blocked.
In practice: prefer autospec=True. It catches more bugs.
AsyncMock
For coroutines, use AsyncMock. Pre-configured to return coroutines.
from unittest.mock import AsyncMock
@patch("myapp.api.fetch_data", new_callable=AsyncMock)
async def test_call(mock_fetch):
mock_fetch.return_value = {"ok": True}
result = await myapp.api.fetch_data()
assert result == {"ok": True}
In Python 3.8+ MagicMock auto-detects async methods on a spec and uses AsyncMock for them.
See 06_pytest_asyncio.md.
Asserting calls
mock.assert_called() # called at least once
mock.assert_called_once() # exactly once
mock.assert_called_with(1, 2, key="x") # last call args
mock.assert_called_once_with(1, 2) # exactly once + args
mock.assert_any_call(...) # any historical call
mock.call_count # int
mock.call_args # last call's call(args, kwargs)
mock.call_args_list # all calls
Common typo bug: assert_called_with requires the underscore — assert mock.called_with(...) silently auto-creates a sub-mock and always passes. Use autospec=True (or from unittest.mock import call) to catch this.
Common patterns
Mock returning a chained call:
mock_get.return_value.json.return_value = {"ok": True}
# now: mock_get(...).json() == {"ok": True}
Recording call sequences:
mock.call_args_list == [
call(1, 2),
call(3, 4),
]
Reset between assertions:
mock.reset_mock() # clears call history; keeps return_value/side_effect
Interview angle
- Q: “Difference between Mock and MagicMock?” — MagicMock pre-configures dunders.
- Q: “What’s
return_valuevsside_effect?” — fixed value vs callable/exception/iterable. - Follow-up: “Where do you patch — at definition or use site?” — use site.
patch("myapp.api.requests.get")notpatch("requests.get"). - Follow-up: “What does
autospec=Truegive you?” — signature checking, attribute restriction, catches “called with wrong args” bugs.
See 01_mocks_external_apis.md for end-to-end mock examples, 09_test_doubles_taxonomy.md for the broader taxonomy.