Skip to content

Python · Testing

Python Mocking & Patching Explained — unittest.mock, patch, and Asserting Calls

4 min read Updated 2026-06-19 Share:

Practice Mocking & Patching interview questions

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.

More ways to practice

The self-quiz is live. Get notified when mock interviews and new question packs drop.

or
Join our WhatsApp Channel