Mocking & Patching Interview Questions & Answers

6 questions Updated 2026-06-18

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.

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.

Practice tests are coming soon

Get notified when interactive mock interviews and quizzes launch.