Skip to content

Mocking & Patching Interview Questions & Answers

15 questions Updated 2026-06-18 Share:

Python interview questions on mocking and why it matters, Mock vs MagicMock, unittest.mock.patch and patching where it's used, return_value vs side_effect, call assertions, and autospec.

Read the in-depth guidePython Mocking & Patching Explained — unittest.mock, patch, and Asserting Calls(opens in new tab)
15 of 15

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 ways to practice

The self-quiz is live. Join our channel for updates, new content & tech tips.

Join our WhatsApp Channel