Mocking And Patching

Deep dive · part of Python Testing

unittest.mock replaces collaborators with controllable doubles. Use patch to swap out modules or attributes for the duration of a test. Always patch where the name is used, not where it is defined.

unittest.mock isolates units under test by replacing collaborators with MagicMock objects or patched attributes. patch('pkg.module.attr') swaps the name where it is looked up, not where it is defined—critical for requests.get used inside your app module.

Mocks verify interactions (assert_called_with) and simulate failures (side_effect). Over-mocking tests implementation, not behavior—mock boundaries: network, clock, filesystem.

Production code combines this topic with logging, tests, and clear module boundaries so refactors stay safe when requirements grow.

patch as context manager or decorator restores original after test.

MagicMock auto-creates attributes; spec= limits to real interface.

return_value sets single result; side_effect iterable or callable for sequences.

patch.object targets one attribute on an existing object.

autospec=True propagates function signature to mock for type checking.

pytest-mock mocker fixture wraps patch with slightly cleaner syntax.

Dependency injection reduces patch needs: pass a fetch callable into functions under test. For datetime, patch datetime.datetime.now at the import location your module uses.

Async code needs AsyncMock (3.8+) for awaitable methods.

Assert final state over call order unless order is protocol-critical—tests stay resilient when internals refactor.

Assert outcomes, not call order, unless the protocol explicitly requires sequential side effects.

Read the parent tutorial on pythondeck.com for runnable snippets, then reproduce them locally in a virtual environment with pinned dependency versions matching your deployment target.

When pairing with teammates, agree on one idiomatic pattern per concern—mixed styles in one repo slow reviews and invite subtle integration bugs during merges.

Patching requests at wrong import path so app still hits network.

Mocks without spec accepting any attribute, hiding API drift.

Forgetting to start patch before importing module under test.

Asserting call order on implementation details instead of outcomes.

Patch where the name is used, verified by grep in the package.

Prefer injecting fakes in constructor for new code.

Use side_effect with real exceptions to test error paths.

Keep one behavioral assertion per test alongside mock checks.

Re-read the examples below with these ideas in mind; change variable names and inputs to match your own project.

The program below demonstrates mock object. Read the comments on each line, run the code, then change names or values to see how the output shifts.

# Example: Mock object
# Run in the REPL or save as a .py file and execute with python.
from unittest.mock import MagicMock
svc = MagicMock()
svc.fetch.return_value = {"ok": True}
print(svc.fetch("/data"))
svc.fetch.assert_called_once_with("/data")

This sample walks through patch in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.

# code under test (app.py):
#   import requests
#   def me():
#       return requests.get("https://api/me").json()

from unittest.mock import patch, MagicMock

with patch("app.requests") as r:
    r.get.return_value = MagicMock(json=lambda: {"name": "ada"})
    import app
    assert app.me() == {"name": "ada"}

Here is a hands-on illustration of side_effect. Follow the inline comments first; only then execute the snippet and compare the result with what you expected.

# Example: side_effect
# Run in the REPL or save as a .py file and execute with python.
from unittest.mock import MagicMock
m = MagicMock()
m.side_effect = [1, 2, ValueError("boom")]
print(m(), m())
try: m()
except ValueError as e: print("got", e)

The program below demonstrates magicmock record. Read the comments on each line, run the code, then change names or values to see how the output shifts.

# MagicMock records calls and allows return_value configuration
from unittest.mock import MagicMock  # mock
api = MagicMock()  # fake API
api.fetch.return_value = {"ok": True}  # stub response
print(api.fetch("/x"))  # {'ok': True}
api.fetch.assert_called_once_with("/x")  # verify call
print(api.fetch.call_count)  # 1
api.reset_mock()  # clear call history for next test
print(api.fetch.call_count)  # 0 after reset

This sample walks through patch target in a small, runnable script. Paste it into the REPL or save it as a .py file before you continue to the next block.

# patch replaces name in namespace where it is looked up
from unittest.mock import patch  # patch
import urllib.request  # module under test

def fetch_url(url):  # wrapper using urlopen
    with urllib.request.urlopen(url, timeout=5) as resp:  # network
        return resp.status  # HTTP code

with patch("urllib.request.urlopen") as mock_open:  # swap urlopen
    mock_open.return_value.__enter__.return_value.status = 200  # context manager
    print(fetch_url("http://example.com"))  # 200 without network

« back to Python Testing All tutorials