Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Isolating Dependencies with unittest.mock

Isolate external collaborators with Mock, side_effect, patch, and autospec while keeping the real behavior under test.

Levels: Intermediate ~30 min read

Intermediate

A unit test should usually exercise the behavior you own without depending on a live network, a paid service, a changing clock, or another system that is outside the test. Python’s built-in unittest.mock module lets you replace a collaborator with a controlled test double and inspect important interactions. This lesson builds on the existing unittest.TestCase lesson; it does not repeat how to create test cases or choose ordinary assertions.

Control a collaborator with Mock

A Mock can return a value you choose and record how it was called. Passing a dependency into a function makes the boundary clear and often avoids patching module globals entirely. Test the result the user cares about, then check a collaborator call only when that interaction is part of the behavior.

Interactive Runnable example
Edit

Ready to run.

The mock replaces only the lookup; the formatting function still runs for real. The test checks both the user-visible result and the input sent to the collaborator. A mock’s default return value is another mock, so set return_value explicitly whenever the returned data matters.

Choose a return value

Which keyword gives a mock a controlled result when it is called?

from unittest.mock import Mock
lookup = Mock(={"name": "Ada"})
assert lookup("user-2") == {"name": "Ada"}

Simulate changing results and failures

Use side_effect when a dependency must behave differently on successive calls, calculate a result from arguments, or raise a specific exception. An iterable is useful for modeling a temporary failure followed by success without contacting a real system. Keep the retry policy itself real so the test still checks your code.

Interactive Runnable example
Edit

Ready to run.

An exception class or instance used as side_effect is raised by the mock. An iterable supplies one outcome per call and raises StopIteration if it runs out of outcomes; provide enough outcomes for the behavior under test. A callable side_effect can instead inspect arguments and return a calculated value.

Model a dependency failure

Which setting makes this mock raise the supplied exception when called?

from unittest.mock import Mock
read = Mock(=TimeoutError("temporary"))
try:
    read()
except TimeoutError:
    result = "retry"
assert result == "retry"

Patch the name that the code looks up

Sometimes code imports or stores a dependency in a particular module or object namespace, so dependency injection is not available. In that case, patch() or patch.object() temporarily replaces the attribute the code actually looks up. For example, if billing.py uses billing.fetch_rate then patch that name—not the original provider’s name in another module. Patches are automatically undone when their decorator or context-manager scope ends, including when the block raises.

Interactive Runnable example
Edit

Ready to run.

The small gateway namespace is a stand-in for a dependency module: total_in_euros() resolves gateway.fetch_rate at call time, so that exact attribute is patched. In ordinary code, target the consumer’s lookup location. Prefer a scoped with patch(...) or a @patch(...) decorator over manually starting a patch and risking that it is never stopped.

Patch the looked-up attribute

Complete the temporary replacement of the attribute that this code calls.

from types import SimpleNamespace
from unittest.mock import patch
client = SimpleNamespace(load=lambda key: "live")
with patch.object(client, , return_value="sample") as mocked:
    assert client.load("item-7") == "sample"
    mocked.assert_called_once_with("item-7")

Constrain mocks with autospec

An unrestricted Mock will happily invent attributes and accept arbitrary call signatures. That flexibility can hide misspellings in tests. create_autospec() builds a mock from a real callable or object so function signatures are checked and unknown attributes are limited to the specification. The autospec=True option offers the same idea when patching. A specification makes a mock safer, but it does not prove that a real external service behaves as expected.

Interactive Runnable example
Edit

Ready to run.

Keep the real function signature

Which call is valid for a callable with signature `publish(channel, message, *, urgent=False)`?

from unittest.mock import create_autospec
def publish(channel, text, *, urgent=False):
    pass
sender = create_autospec(publish)
sender()
sender.assert_called_once_with("news", "done", urgent=True)

Assert important interactions—and avoid over-mocking

Interaction assertions are valuable when a call itself matters: sending one notification, saving once, or not contacting a service for a rejected request. assert_not_called() checks a negative interaction. Avoid asserting every incidental method call or replacing the code under test with a mock; over-mocked tests can pass while the real behavior is broken. Keep pure transformations real, mock boundaries that would be slow, unreliable, costly, or unsafe, and retain separate tests for integration with the real boundary.

Interactive Runnable example
Edit

Ready to run.

Check that no call occurred

Which assertion verifies that a rejected record did not call the notification dependency?

from unittest.mock import Mock
send = Mock()
result = False
if result:
    send("case-9")
send.()

Remember these boundaries

  • Use Mock(return_value=...) for predictable collaborator results and assert meaningful calls.
  • Use side_effect for outcomes that vary by call or for simulated exceptions.
  • Patch the name the code under test actually looks up, inside a scope that restores it.
  • Prefer autospec when it fits, and mock external boundaries rather than the behavior being tested.
Was this page helpful?

Mark this learning resource complete to track your learning progress.