Mock
02 / 02

patch(), spec/autospec & Testing Pitfalls

Mock: patch(), spec/autospec & Testing Pitfalls

patch(): Temporary Substitution

# mymodule.py
import requests

def fetch_user(user_id):
    response = requests.get(f"https://api.example.com/users/{user_id}")
    return response.json()

# test_mymodule.py -- decorator form, scoped to the whole test function
from unittest.mock import patch

# CRITICAL: patch where the name is LOOKED UP (mymodule.requests.get),
# not where requests.get was originally defined. mymodule did
# `import requests` and calls requests.get -- that's the binding
# to patch. patching 'requests.get' directly would NOT affect
# mymodule's own reference to it.
@patch('mymodule.requests.get')
def test_fetch_user(mock_get):
    mock_get.return_value.json.return_value = {"id": 1, "name": "Alice"}
    result = fetch_user(1)
    assert result == {"id": 1, "name": "Alice"}
    mock_get.assert_called_once_with("https://api.example.com/users/1")

# Context manager form -- scoped to just the `with` block
def test_fetch_user_context():
    with patch('mymodule.requests.get') as mock_get:
        mock_get.return_value.json.return_value = {"id": 2}
        assert fetch_user(2) == {"id": 2}
    # original requests.get is restored here, automatically

# patch.object -- same effect, takes an object reference instead of a string path
with patch.object(SomeClass, 'method_name') as mock_method:
    ...

# Class-decorator form -- applies to EVERY test method in the class
@patch('mymodule.requests.get')
class TestUserFetching(unittest.TestCase):
    def test_one(self, mock_get): ...
    def test_two(self, mock_get): ...

spec, autospec & PropertyMock

# Plain Mock accepts ANY attribute/call -- a typo silently "works"
plain = Mock()
plain.fethc_user()  # typo, but no error -- just returns another Mock

# spec restricts the mock to the real class's actual attributes
from unittest.mock import create_autospec

specced = Mock(spec=RealApiClient)
specced.fethc_user()  # AttributeError -- 'fethc_user' doesn't exist on RealApiClient

# create_autospec goes further: also validates call SIGNATURES,
# catching a wrong-argument-count call like the real callable would
auto = create_autospec(RealApiClient)
auto.fetch_user()  # TypeError -- missing required argument

# PropertyMock -- needed to correctly mock a @property (a descriptor,
# not a regular callable attribute)
from unittest.mock import PropertyMock

with patch.object(type(obj), 'some_property', new_callable=PropertyMock) as mock_prop:
    mock_prop.return_value = 'mocked value'
    assert obj.some_property == 'mocked value'

Why Mock External Dependencies

  • Real network/DB calls in unit tests are slow, flaky (network availability, external uptime), and sometimes costly (rate limits, paid API usage).

  • Mocking isolates the test to the code under test's own logic -- fast, deterministic, offline.

  • Risk of over-mocking: tests can keep passing against a stale mock configuration even after the real dependency's actual behavior changes -- complement with some integration/contract tests exercising real dependencies.

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free