Last Updated: June 01, 2026
Intermediate
You have written a function that sends emails, calls a third-party API, or reads from a database. You want to write unit tests for it, but running those tests would actually send emails, hit the live API, and modify real data. Worse, the tests would be slow, flaky (depending on network availability), and expensive (API calls cost money). The solution is mocking: replacing the real dependency with a controlled fake that behaves exactly how you tell it to.
Python’s unittest.mock module is the standard library’s built-in mocking toolkit. It ships with Python 3.3+ at no extra installation cost and integrates seamlessly with both unittest and pytest. The module provides Mock and MagicMock objects that record all calls made to them, plus a patch() decorator and context manager that temporarily swaps real objects with mocks in your code’s namespace.
This article covers the complete mocking workflow: creating basic mocks, configuring return values and side effects, using patch() to intercept dependencies, mocking HTTP requests, mocking time, and asserting on mock call history. By the end, you will be able to test any function in isolation, no matter what external systems it depends on.
Python developer and educator with 15+ years building production systems across data engineering, web APIs, and AI tooling. Founder of Python How To Program — 270+ in-depth tutorials covering the modern Python stack.
View all tutorials by Pubs →unittest.mock Quick Example
Part of the Python Testing & Quality Hub. See the full hub for related Python tutorials.
Here is the pattern in its simplest form: a function that calls an external API, tested without hitting any network.
# quick_mock_example.py
import unittest
from unittest.mock import patch, MagicMock
def get_user_name(user_id: int) -> str:
"""Fetches a user name from an external API (real implementation)."""
import requests
response = requests.get(f"https://jsonplaceholder.typicode.com/users/{user_id}")
response.raise_for_status()
return response.json()["name"]
class TestGetUserName(unittest.TestCase):
@patch("quick_mock_example.requests.get")
def test_returns_user_name(self, mock_get):
# Configure the fake response
mock_response = MagicMock()
mock_response.json.return_value = {"id": 1, "name": "Leanne Graham"}
mock_response.raise_for_status.return_value = None
mock_get.return_value = mock_response
# Call the function under test
result = get_user_name(1)
# Assertions
self.assertEqual(result, "Leanne Graham")
mock_get.assert_called_once_with(
"https://jsonplaceholder.typicode.com/users/1"
)
if __name__ == "__main__":
unittest.main()
Output:
python -m pytest quick_mock_example.py -v
quick_mock_example.py::TestGetUserName::test_returns_user_name PASSED
1 passed in 0.04s
The test completes in 40 milliseconds with zero network calls. The @patch() decorator intercepts requests.get in the module under test and replaces it with a MagicMock. The mock records the call and returns exactly what we told it to. The deeper sections explain each component in detail.
Mock and MagicMock: The Foundation
A Mock object accepts any attribute access and any method call without raising errors. Every call is recorded. MagicMock is a subclass that additionally supports Python’s magic methods (__len__, __iter__, __enter__, etc.), making it suitable for mocking context managers and containers.
# mock_basics.py
from unittest.mock import Mock, MagicMock
# Any attribute access returns another Mock
m = Mock()
print(m.anything) #
print(m.foo.bar.baz()) #
# Configure return values
m.get_price.return_value = 9.99
print(m.get_price()) # 9.99
# Raise an exception
m.failing_call.side_effect = ValueError("Something went wrong")
try:
m.failing_call()
except ValueError as e:
print(e) # Something went wrong
# Check call history
m.calculate(10, 20)
print(m.calculate.called) # True
print(m.calculate.call_count) # 1
print(m.calculate.call_args) # call(10, 20)
# MagicMock supports __len__, __iter__, context manager
mm = MagicMock()
mm.__len__.return_value = 5
print(len(mm)) # 5
mm.__iter__.return_value = iter([1, 2, 3])
print(list(mm)) # [1, 2, 3]
Output:
9.99
Something went wrong
True
1
call(10, 20)
5
[1, 2, 3]
The key distinction between Mock and MagicMock: use MagicMock by default (it covers everything Mock does plus magic methods), and only drop to Mock if you specifically want attribute access on undefined magic methods to raise AttributeError rather than silently returning another Mock.
The patch() Function: Intercepting Dependencies
patch() is the workhorse of unittest.mock. It temporarily replaces an object in a specific module’s namespace for the duration of a test, then restores it afterward. The target string must be the full dotted path to the object as it is imported in the code under test — not where it is originally defined.
patch() as a Decorator
# patch_decorator.py
import unittest
from unittest.mock import patch, MagicMock
# --- Code under test ---
import os
def get_home_directory() -> str:
return os.path.expanduser("~")
def read_config_file(path: str) -> dict:
import json
with open(path) as f:
return json.load(f)
# --- Tests ---
class TestWithPatch(unittest.TestCase):
@patch("patch_decorator.os.path.expanduser")
def test_get_home(self, mock_expand):
mock_expand.return_value = "/home/testuser"
result = get_home_directory()
self.assertEqual(result, "/home/testuser")
mock_expand.assert_called_once_with("~")
@patch("builtins.open", new_callable=MagicMock)
@patch("patch_decorator.json.load")
def test_read_config(self, mock_json_load, mock_open):
mock_json_load.return_value = {"debug": True, "port": 8080}
result = read_config_file("/fake/config.json")
self.assertEqual(result["port"], 8080)
mock_open.assert_called_once_with("/fake/config.json")
if __name__ == "__main__":
unittest.main()
Output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.003s
OK
When stacking multiple @patch() decorators, the mocks are passed to the test function in bottom-up order — the bottom-most decorator’s mock is the first argument. This is a common source of confusion. In the example above, mock_json_load comes from the lower @patch("patch_decorator.json.load") and mock_open from the upper @patch("builtins.open").
patch() as a Context Manager
Use the context manager form when you only need the mock for part of a test, or when you are not using a test class.
# patch_context.py
from unittest.mock import patch
import pytest
def send_notification(message: str, email: str) -> bool:
"""Sends an email notification. Returns True on success."""
import smtplib
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.sendmail("noreply@example.com", email, message)
return True
def test_send_notification_success():
with patch("patch_context.smtplib.SMTP") as mock_smtp_class:
mock_server = mock_smtp_class.return_value.__enter__.return_value
mock_server.sendmail.return_value = {}
result = send_notification("Hello!", "user@example.com")
assert result is True
mock_server.starttls.assert_called_once()
mock_server.sendmail.assert_called_once_with(
"noreply@example.com", "user@example.com", "Hello!"
)
Output:
pytest patch_context.py -v
patch_context.py::test_send_notification_success PASSED
Mocking a context manager requires one extra step: you need to mock the object returned by __enter__. In the example above, mock_smtp_class.return_value is what smtplib.SMTP("smtp.gmail.com", 587) returns, and .__enter__.return_value is the server variable inside the with block. This chain is the standard pattern for any with statement mock.
Side Effects: Sequences, Exceptions, and Callables
The side_effect attribute gives you fine-grained control over what happens when a mock is called. You can make it raise exceptions, return different values on successive calls, or run a custom function.
# side_effects.py
from unittest.mock import Mock, patch
import unittest
class TestSideEffects(unittest.TestCase):
def test_raise_on_third_call(self):
"""Simulate a flaky API that fails on the 3rd attempt."""
mock_api = Mock()
mock_api.side_effect = [
{"status": "ok", "data": "first"},
{"status": "ok", "data": "second"},
ConnectionError("API unreachable"),
]
self.assertEqual(mock_api()["data"], "first")
self.assertEqual(mock_api()["data"], "second")
with self.assertRaises(ConnectionError):
mock_api()
def test_dynamic_response(self):
"""Return different responses based on the input."""
def dynamic(url):
if "users" in url:
return {"type": "user", "id": 1}
return {"type": "unknown"}
mock_get = Mock(side_effect=dynamic)
self.assertEqual(mock_get("https://api.example.com/users/1")["type"], "user")
self.assertEqual(mock_get("https://api.example.com/other")["type"], "unknown")
if __name__ == "__main__":
unittest.main()
Output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.001s
OK
The list-based side_effect is extremely useful for testing retry logic: set it to a list where the first N elements are exceptions and the last element is the successful response. Your retry function should exhaust the exceptions and succeed on the final call. If the mock runs out of values in the list, it raises StopIteration on the next call.
Asserting on Calls
After running the code under test, verify that the mock was called correctly. The assertion methods are specific enough to catch argument order mistakes and missing calls.
# assert_calls.py
from unittest.mock import Mock, call
mock_logger = Mock()
# Simulate some calls
mock_logger.info("Server started on port 8080")
mock_logger.warning("Disk usage at 85%")
mock_logger.info("Request received from 192.168.1.1")
# Basic call assertions
mock_logger.info.assert_called() # Was it called at all?
mock_logger.warning.assert_called_once() # Called exactly once?
mock_logger.error.assert_not_called() # Was it NOT called?
# Argument assertions
mock_logger.warning.assert_called_with("Disk usage at 85%")
# Assert all calls in order
mock_logger.info.assert_has_calls([
call("Server started on port 8080"),
call("Request received from 192.168.1.1"),
])
# Get full call history
print(mock_logger.info.call_args_list)
# [call('Server started on port 8080'), call('Request received from 192.168.1.1')]
print(mock_logger.call_count) # Total calls across all methods
print(mock_logger.call_args_list) # All calls in order
Output:
[call('Server started on port 8080'), call('Request received from 192.168.1.1')]
3
[call.info('Server started on port 8080'), call.warning('Disk usage at 85%'), call.info('Request received from 192.168.1.1')]
Use assert_called_once_with() (not assert_called_with()) when you need to verify both the arguments AND that the function was called exactly once. assert_called_with() only checks the most recent call’s arguments — if the mock was called 10 times, it passes as long as the last call matches.
Real-Life Example: Testing a Weather Service Client
This project tests a complete weather service client that fetches data from an external API and caches results — no network required.
# weather_service.py
import requests
from datetime import datetime
class WeatherService:
"""Fetches weather data from an external API."""
BASE_URL = "https://api.open-meteo.com/v1/forecast"
def __init__(self):
self._cache = {}
def get_temperature(self, latitude: float, longitude: float) -> float:
"""Returns current temperature in Celsius. Raises on HTTP error."""
cache_key = (round(latitude, 2), round(longitude, 2))
if cache_key in self._cache:
return self._cache[cache_key]
params = {
"latitude": latitude,
"longitude": longitude,
"current_weather": True,
}
response = requests.get(self.BASE_URL, params=params)
response.raise_for_status()
data = response.json()
temp = data["current_weather"]["temperature"]
self._cache[cache_key] = temp
return temp
def is_warm(self, latitude: float, longitude: float) -> bool:
"""Returns True if temperature is above 20 C."""
return self.get_temperature(latitude, longitude) > 20.0
# --- Tests ---
import unittest
from unittest.mock import patch, MagicMock
class TestWeatherService(unittest.TestCase):
def setUp(self):
self.service = WeatherService()
@patch("weather_service.requests.get")
def test_get_temperature_success(self, mock_get):
mock_response = MagicMock()
mock_response.json.return_value = {
"current_weather": {"temperature": 23.5, "windspeed": 12.0}
}
mock_response.raise_for_status.return_value = None
mock_get.return_value = mock_response
temp = self.service.get_temperature(48.85, 2.35)
self.assertEqual(temp, 23.5)
@patch("weather_service.requests.get")
def test_caches_result(self, mock_get):
mock_response = MagicMock()
mock_response.json.return_value = {"current_weather": {"temperature": 18.0}}
mock_response.raise_for_status.return_value = None
mock_get.return_value = mock_response
self.service.get_temperature(51.5, -0.12)
self.service.get_temperature(51.5, -0.12) # Second call should use cache
mock_get.assert_called_once() # API called only once
@patch("weather_service.requests.get")
def test_raises_on_http_error(self, mock_get):
mock_response = MagicMock()
mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError("404")
mock_get.return_value = mock_response
with self.assertRaises(requests.exceptions.HTTPError):
self.service.get_temperature(0, 0)
@patch.object(WeatherService, "get_temperature", return_value=22.0)
def test_is_warm_true(self, mock_temp):
self.assertTrue(self.service.is_warm(48.85, 2.35))
@patch.object(WeatherService, "get_temperature", return_value=15.0)
def test_is_warm_false(self, mock_temp):
self.assertFalse(self.service.is_warm(48.85, 2.35))
if __name__ == "__main__":
unittest.main(verbose=2)
Output:
test_caches_result ... ok
test_get_temperature_success ... ok
test_is_warm_false ... ok
test_is_warm_true ... ok
test_raises_on_http_error ... ok
----------------------------------------------------------------------
Ran 5 tests in 0.008s
OK
Note the use of @patch.object() in the last two tests — it patches a method directly on a class instance, which is cleaner than patching through the module path when the class is in the same file. This pattern is especially useful when testing methods that call other methods on the same object, letting you test is_warm() independently of get_temperature().
Frequently Asked Questions
Where exactly should I patch — the import source or the import location?
Always patch where the name is used, not where it is defined. If mymodule.py does import requests and uses requests.get(), patch "mymodule.requests.get", not "requests.get". This is the single most common mocking mistake. The rule is: patch the name in the namespace of the code you are testing, because Python looks up names in their own module’s namespace at runtime.
What is autospec and when should I use it?
Pass spec=SomeClass or autospec=True to patch() to create a mock that enforces the real object’s interface. If you call the mock with wrong arguments or access an attribute that does not exist on the real object, the mock raises AttributeError or TypeError immediately. This catches tests that pass because the mock accepts anything, even when the real code would fail. Use autospec=True for production code; skip it for quick exploratory tests.
Should I use unittest.mock or pytest-mock?
Both wrap the same underlying unittest.mock machinery. The pytest-mock package provides a mocker fixture that cleans up automatically and integrates more naturally with pytest‘s fixture system. If your project uses pytest, pytest-mock is slightly more ergonomic: mocker.patch("module.thing") vs the @patch decorator. Either works — the choice is stylistic.
How do I mock datetime.now()?
You cannot patch datetime.datetime.now directly because datetime is a C extension. Instead, either use the freezegun library (@freeze_time("2024-01-01") — covered in the freezegun article) or wrap datetime.now() in your own function and mock that wrapper. For most projects, freezegun is the cleaner solution since it handles all time-related calls in your code automatically.
How do I reset a mock between tests?
Call mock.reset_mock() to clear call history, or simply create a new Mock() in each test’s setUp method. If you are using @patch as a decorator, the mock is automatically fresh for each test because the decorator creates a new mock on each test run. Only manually reset mocks when you are reusing the same mock object across multiple assertions within a single test.
Conclusion
Mocking transforms slow, flaky integration tests into fast, reliable unit tests. You have covered creating Mock and MagicMock objects, configuring return values and side effects, using patch() as both a decorator and context manager, asserting on call history, and applying all of it to a realistic weather service client with five passing tests in 8 milliseconds. The key rule to remember: always patch where the name is used, not where it is defined.
Extend the weather service tests by adding a test for the cache key rounding behaviour (try coordinates that differ only in the third decimal place), and add a test that verifies the HTTP parameters passed to requests.get() using assert_called_once_with(). These edge cases are exactly what mocking was designed to cover cheaply and reliably.
For the complete API reference, see the official unittest.mock documentation.
Why Mock Dependencies?
Tests should be fast, deterministic, and isolated. A test that hits a real database is slow; one that hits an external API is flaky; one that depends on the current time is non-deterministic. Mocks replace those dependencies with predictable stand-ins:
from unittest.mock import Mock
# Create a mock object
api_client = Mock()
api_client.fetch_user.return_value = {"id": 1, "name": "Alice"}
# Use it in your code — looks like the real thing
user = api_client.fetch_user(42)
print(user) # {'id': 1, 'name': 'Alice'}
# Inspect what happened
print(api_client.fetch_user.called) # True
print(api_client.fetch_user.call_args) # call(42)
print(api_client.fetch_user.call_count) # 1
Mock auto-creates attributes — mock.anything.you.access just works. The mock records every interaction so you can assert what happened.
patch() Decorator and Context Manager
from unittest.mock import patch
# Decorator form
@patch("myapp.module.send_email")
def test_signup(mock_send):
mock_send.return_value = True
result = signup_user("alice@example.com")
mock_send.assert_called_once_with("alice@example.com", "Welcome")
assert result == "signed up"
# Context manager form
def test_other():
with patch("myapp.module.send_email") as mock_send:
mock_send.return_value = True
signup_user("alice@example.com")
patch() replaces an attribute during the test, restores it after. The argument is the dotted path to where the function is LOOKED UP, not where it’s defined — a common source of confusion.
Where to Patch: The Most Common Gotcha
# myapp/services.py
from myapp.email import send_email
def signup_user(email):
send_email(email, "Welcome")
# CORRECT — patch where send_email is USED
@patch("myapp.services.send_email")
def test_signup(mock_send):
signup_user("alice@example.com")
mock_send.assert_called_once()
# WRONG — this patches the definition, but services.py has its own reference
@patch("myapp.email.send_email")
def test_signup(mock_send):
signup_user("alice@example.com")
mock_send.assert_not_called() # surprise — wasn't called
The rule: patch the module where the function is REFERENCED, not where it’s defined.
Mocking Side Effects
from unittest.mock import Mock, patch
# Return different values for successive calls
mock = Mock()
mock.side_effect = [1, 2, 3]
print(mock()) # 1
print(mock()) # 2
print(mock()) # 3
# Raise an exception
mock = Mock()
mock.side_effect = ConnectionError("network down")
try:
mock()
except ConnectionError as e:
print(e)
# Side effect as a function
def custom_logic(value):
return value * 2 if value > 0 else 0
mock = Mock(side_effect=custom_logic)
print(mock(5)) # 10
print(mock(-3)) # 0
MagicMock vs Mock
from unittest.mock import MagicMock
# MagicMock auto-implements magic methods (__len__, __iter__, __enter__, etc.)
m = MagicMock()
m.__len__.return_value = 3
print(len(m)) # 3
# Useful for mocking context managers
with MagicMock() as m:
pass # __enter__ and __exit__ work
# Iteration
m.__iter__.return_value = iter([1, 2, 3])
for x in m:
print(x)
MagicMock is Mock + sensible defaults for dunder methods. Use it for objects you’ll use with with, for, len(), etc. The plain Mock doesn’t auto-support those.
Assertions on Mock Calls
mock = Mock()
mock(1, 2, x="hi")
mock(3, 4)
# Was it called at all?
mock.assert_called()
# Was it called exactly once?
mock.assert_called_once()
# Was it called with specific arguments?
mock.assert_called_with(3, 4) # the LAST call
mock.assert_any_call(1, 2, x="hi") # ANY of the calls
# Was it called exactly once with specific arguments?
mock.assert_called_once_with(3, 4) # raises — was called twice
# Call counts and arg history
mock.call_count # 2
mock.call_args_list # [call(1, 2, x='hi'), call(3, 4)]
Common Pitfalls
- Patching the wrong path. Patch where the function is USED, not where defined. Single most common mock mistake.
- Forgetting to reset. Mock state persists across tests if you reuse module-level mocks. Use patch() so cleanup is automatic, or call
mock.reset_mock(). - Asserting on a non-call. Calling
mock.fooauto-creates a child mock;mock.foo.assert_called()succeeds even if the test code never called foo. Use spec=Class to lock down the interface. - Mock not iterable. Plain Mock isn’t. Use MagicMock or set
mock.__iter__.return_value = iter(...). - Side effect with return_value. If both are set, side_effect wins. Pick one.
FAQ
Q: unittest.mock or pytest-mock?
A: pytest-mock wraps unittest.mock with a fixture (mocker) that integrates cleanly with pytest. Same engine; cleaner ergonomics for pytest users.
Q: Mock vs MagicMock vs AsyncMock?
A: Mock for basic; MagicMock for dunder methods; AsyncMock (3.8+) for async callables that return coroutines.
Q: How do I mock an async function?
A: AsyncMock(return_value=...). The patched function becomes an awaitable returning your value.
Q: spec= argument — why use it?
A: Mock(spec=SomeClass) rejects attribute access not on SomeClass. Prevents typos that pass silently. Always recommended.
Q: How do I verify call order across mocks?
A: from unittest.mock import call; mock_a.assert_has_calls([call(1), call(2)]); for cross-mock order use a manager: mgr = Mock(); mgr.attach_mock(mock_a, 'a').
Wrapping Up
Mocking is essential for fast, deterministic, isolated tests. Master patch() with the right target path, Mock vs MagicMock vs AsyncMock, and the assertion family. Use spec= to lock interfaces and catch typos. For pytest users, pytest-mock‘s mocker fixture is the cleaner ergonomics. With these tools, your test suite stays fast and your mocks don’t lie.
Related Articles
Continue Learning Python
Tutorials you might also find useful: