Unit tests are only valuable when they test one thing in isolation. The moment your function reaches out to a database, a live API, the file system, or the system clock, your test is no longer testing your code alone — it is testing every dependency your code touches. @unittest.mock.patch solves this by temporarily swapping any real object in your code's namespace for a controlled substitute, called a mock, for the exact duration of a single test.
The term mock is often used loosely to mean any substitute object in a test. In practice there is a more precise vocabulary, originating in Gerard Meszaros's work on xUnit test patterns. Understanding where patch fits in this vocabulary clarifies what you should reach for and when.
wraps= in patch creates a spy.patch().patch; written by hand.None or any sentinel value.Knowing this taxonomy prevents overuse of patch. If you only need a fixed return value and will never assert on the call, a stub is sufficient — a plain MagicMock(return_value=x) without any assert_called usage. If you need the real function to run but want to observe it, that is the wraps= spy pattern. Reserve full mocking — and therefore full patch() — for cases where you need both: controlled behavior and call verification.
unittest.mock has been part of Python's standard library since Python 3.3. The module provides the Mock class, the MagicMock class, and several patching helpers. patch is the most widely used of those helpers. It is a decorator, a context manager, and a callable that returns a patcher object — all three interfaces share the same underlying mechanism. This article walks through each pattern with full, runnable code examples and explains every parameter worth knowing.
What patch Is and How It Works
At the lowest level, patch is a function that performs two operations on a target attribute: it saves the original value, replaces it with a mock, and registers a cleanup function that restores the original when the test scope ends. Python's module system makes this possible because every name a module has imported lives as an attribute on that module object. patch uses getattr to retrieve the original and setattr to install the replacement. When the scope ends, it calls setattr again with the saved original.
The target you provide to patch is a dotted string in the format 'package.module.attribute'. The string must be importable from the environment where the test runs.
The target is imported when the decorated test function is called, not when the decorator is applied at class definition time. This distinction matters when the module containing the target is imported lazily.
The lookup rule: patch where the name is used, not where it is defined
This is the rule that causes the most confusion. Suppose you have a module called reports.py that imports datetime to stamp the current date onto a generated report:
# reports.py
import datetime
def generate_report(title):
today = datetime.date.today()
return f"{title} — generated on {today}"
When generate_report calls datetime.date.today(), Python looks up the name datetime in reports's namespace, then calls .date.today() on whatever object it finds there. The correct target string is therefore 'reports.datetime', because that is the name inside the module where the lookup actually happens at runtime.
If instead you had written from datetime import date inside reports.py, the local name in reports's namespace would be date, not datetime. In that case the correct target would be 'reports.date'. Patching 'datetime.date' directly would replace the object in the datetime module's namespace, but reports.py already holds its own binding to the original, so your patch would have no effect on it.
# reports.py — version 2: uses a direct import
from datetime import date
def generate_report(title):
today = date.today()
return f"{title} — generated on {today}"
# test_reports.py
import unittest
from unittest.mock import patch
import datetime
import reports
class TestGenerateReport(unittest.TestCase):
# WRONG: patches datetime.date in the datetime module itself,
# but reports.py already has its own 'date' binding — no effect.
@patch('datetime.date')
def test_wrong_target(self, mock_date):
mock_date.today.return_value = datetime.date(2026, 1, 1)
result = reports.generate_report("Q1 Summary")
# The real today() is still called; this test does not control the date.
# CORRECT: patches the 'date' name inside reports's own namespace.
@patch('reports.date')
def test_correct_target(self, mock_date):
mock_date.today.return_value = datetime.date(2026, 1, 1)
result = reports.generate_report("Q1 Summary")
self.assertEqual(result, "Q1 Summary — generated on 2026-01-01")
if __name__ == '__main__':
unittest.main()
Enter your module name, what you want to patch, and how it was imported. The resolver outputs the correct patch target string.
Mock vs MagicMock
When patch replaces your target object, it installs a MagicMock by default. MagicMock is a subclass of Mock with all common dunder methods pre-configured, which means it can be used wherever Python expects magic method support — for example, as a context manager, as an iterable, or in a with statement. A plain Mock does not have these pre-built, so you would need to configure them yourself. For the overwhelming majority of patching scenarios, the default MagicMock is the right choice. Use plain Mock when you specifically want to verify that a mock is not a callable context manager or iterable, which protects against accidentally passing your mock in places that would silently consume those magic methods.
| Class | Dunder methods pre-built | Default in patch() | Typical use |
|---|---|---|---|
Mock |
None — must configure manually | No | Simple callable stubs with no magic method needs |
MagicMock |
Yes — __str__, __len__, __enter__, __exit__, and more | Yes | Context managers, iterables, and general-purpose patching |
AsyncMock |
Yes — async variants of call and context manager protocol | Yes, for async callables | Coroutines and async context managers |
NonCallableMock |
None | No | Patching non-callable attributes like constants or properties |
async def coroutine function?app/mailer.py contains from smtplib import SMTP. Which patch target is correct?Three Ways to Call patch
1. As a decorator on a test method
This is the most common form. The decorator wraps the test function, starts the patcher before the function body runs, and stops it afterward. Each @patch decorator adds one positional argument to the test method. The argument receives the mock object that was installed in place of the target:
# mailer.py
import smtplib
def send_welcome_email(address):
with smtplib.SMTP('smtp.example.com', 587) as server:
server.sendmail('[email protected]', address, 'Welcome!')
return True
# test_mailer.py
import unittest
from unittest.mock import patch, MagicMock
import mailer
class TestSendWelcomeEmail(unittest.TestCase):
@patch('mailer.smtplib.SMTP')
def test_sends_to_correct_address(self, mock_smtp_class):
# SMTP() is now mock_smtp_class.
# Calling smtplib.SMTP(...) returns mock_smtp_class.return_value,
# which is itself a MagicMock that supports the context manager protocol.
mock_server = mock_smtp_class.return_value.__enter__.return_value
result = mailer.send_welcome_email('[email protected]')
self.assertTrue(result)
mock_smtp_class.assert_called_once_with('smtp.example.com', 587)
mock_server.sendmail.assert_called_once_with(
'[email protected]', '[email protected]', 'Welcome!'
)
if __name__ == '__main__':
unittest.main()
Stacking multiple decorators
When you need to patch more than one object in the same test, stack @patch decorators. Decorators are applied bottom-up — the decorator closest to the def line is applied first, which means it corresponds to the first extra argument in the method signature. The topmost decorator corresponds to the last argument.
# uploader.py
import os
import requests
def upload_file(path):
if not os.path.exists(path):
raise FileNotFoundError(path)
with open(path, 'rb') as f:
response = requests.post('https://api.example.com/upload', data=f)
return response.status_code
# test_uploader.py
import unittest
from unittest.mock import patch, MagicMock, mock_open
import uploader
class TestUploadFile(unittest.TestCase):
# Bottom decorator → first mock argument (mock_os)
# Top decorator → last mock argument (mock_post)
@patch('uploader.requests.post') # applied second → mock_post
@patch('uploader.os.path.exists') # applied first → mock_exists
def test_successful_upload(self, mock_exists, mock_post):
mock_exists.return_value = True
mock_response = MagicMock()
mock_response.status_code = 200
mock_post.return_value = mock_response
with patch('builtins.open', mock_open(read_data=b'file content')):
status = uploader.upload_file('/tmp/report.pdf')
self.assertEqual(status, 200)
mock_exists.assert_called_once_with('/tmp/report.pdf')
mock_post.assert_called_once()
@patch('uploader.requests.post')
@patch('uploader.os.path.exists')
def test_raises_when_file_missing(self, mock_exists, mock_post):
mock_exists.return_value = False
with self.assertRaises(FileNotFoundError):
uploader.upload_file('/tmp/missing.pdf')
# requests.post should never have been called
mock_post.assert_not_called()
if __name__ == '__main__':
unittest.main()
2. As a context manager
Using patch as a context manager with the with statement gives you fine-grained control over exactly which lines of code run with the mock installed. The mock is only active for the duration of the with block:
# test_reports_context.py
import unittest
from unittest.mock import patch
import datetime
import reports
class TestGenerateReportContextManager(unittest.TestCase):
def test_report_date_is_controlled(self):
fixed_date = datetime.date(2026, 6, 15)
with patch('reports.date') as mock_date:
mock_date.today.return_value = fixed_date
result = reports.generate_report("Mid-Year Review")
# Outside the with block, reports.date is the real datetime.date again.
self.assertIn("2026-06-15", result)
self.assertIn("Mid-Year Review", result)
if __name__ == '__main__':
unittest.main()
The context manager form is particularly readable when you have complex setup logic that should only partially run under a mock, or when you want to mix real and mocked calls within the same test method.
3. As a patcher started and stopped manually
For situations where you need a mock to stay active across multiple test methods — such as in setUp and tearDown — you can call patch directly to get a patcher object, start it explicitly, and stop it in cleanup:
# test_service_setup.py
import unittest
from unittest.mock import patch, MagicMock
import mailer
class TestMailerWithSetup(unittest.TestCase):
def setUp(self):
# Start the patcher and register cleanup so tearDown is never forgotten.
patcher = patch('mailer.smtplib.SMTP')
self.mock_smtp_class = patcher.start()
self.addCleanup(patcher.stop) # addCleanup is safer than tearDown
# Configure a shared mock server instance for all test methods.
self.mock_server = self.mock_smtp_class.return_value.__enter__.return_value
def test_sends_correct_subject(self):
mailer.send_welcome_email('[email protected]')
self.mock_server.sendmail.assert_called_once()
def test_smtp_connects_to_correct_host(self):
mailer.send_welcome_email('[email protected]')
self.mock_smtp_class.assert_called_with('smtp.example.com', 587)
if __name__ == '__main__':
unittest.main()
Always prefer self.addCleanup(patcher.stop) over tearDown when manually starting patchers. addCleanup runs even if setUp raises an exception partway through, so it is impossible to forget a stop call and leak a patched object into subsequent tests.
patch.object — patching by object reference
patch.object takes a live Python object and a string attribute name instead of a dotted path string. This is useful when you already have a reference to the class or module you want to patch, and you want your test to fail at load time if the attribute does not exist rather than at runtime:
# payment.py
import stripe
class PaymentProcessor:
def charge(self, amount_cents, token):
charge = stripe.Charge.create(
amount=amount_cents,
currency='usd',
source=token,
)
return charge['id']
# test_payment.py
import unittest
from unittest.mock import patch, MagicMock
import stripe
from payment import PaymentProcessor
class TestPaymentProcessor(unittest.TestCase):
@patch.object(stripe.Charge, 'create')
def test_charge_returns_charge_id(self, mock_create):
mock_create.return_value = {'id': 'ch_test_abc123', 'status': 'succeeded'}
processor = PaymentProcessor()
charge_id = processor.charge(5000, 'tok_visa')
self.assertEqual(charge_id, 'ch_test_abc123')
mock_create.assert_called_once_with(
amount=5000,
currency='usd',
source='tok_visa',
)
if __name__ == '__main__':
unittest.main()
patch.dict — patching dictionaries and os.environ
patch.dict patches a dictionary for the duration of the test. Entries you provide are merged in; entries absent from your patch remain unchanged. When the test ends, the dictionary is restored to its original state. This is the standard way to control environment variables in tests without touching os.environ directly:
# config.py
import os
def get_database_url():
url = os.environ.get('DATABASE_URL')
if not url:
raise EnvironmentError("DATABASE_URL is not set")
return url
# test_config.py
import unittest
import os
from unittest.mock import patch
from config import get_database_url
class TestGetDatabaseUrl(unittest.TestCase):
@patch.dict(os.environ, {'DATABASE_URL': 'postgresql://localhost/testdb'})
def test_returns_database_url(self):
url = get_database_url()
self.assertEqual(url, 'postgresql://localhost/testdb')
@patch.dict(os.environ, {}, clear=True) # clear=True removes ALL env vars
def test_raises_when_var_missing(self):
with self.assertRaises(EnvironmentError):
get_database_url()
if __name__ == '__main__':
unittest.main()
Configuring Mocks: return_value, side_effect, and Assertions
Installing a mock is only half the work. The other half is configuring what the mock does when called, and then asserting that your code interacted with it in the expected way.
return_value
return_value is the value a mock returns every time it is called as a function. You can set it at construction time or afterward. When you patch a class, return_value represents what the class constructor returns — that is, the instance:
# inventory.py
import boto3
def list_s3_objects(bucket):
s3 = boto3.client('s3')
response = s3.list_objects_v2(Bucket=bucket)
return [obj['Key'] for obj in response.get('Contents', [])]
# test_inventory.py
import unittest
from unittest.mock import patch, MagicMock
import inventory
class TestListS3Objects(unittest.TestCase):
@patch('inventory.boto3.client')
def test_returns_object_keys(self, mock_client_constructor):
# mock_client_constructor() is what boto3.client('s3') returns.
mock_s3 = mock_client_constructor.return_value
mock_s3.list_objects_v2.return_value = {
'Contents': [
{'Key': 'reports/q1.pdf'},
{'Key': 'reports/q2.pdf'},
]
}
keys = inventory.list_s3_objects('my-data-bucket')
self.assertEqual(keys, ['reports/q1.pdf', 'reports/q2.pdf'])
mock_client_constructor.assert_called_once_with('s3')
mock_s3.list_objects_v2.assert_called_once_with(Bucket='my-data-bucket')
@patch('inventory.boto3.client')
def test_returns_empty_list_when_bucket_empty(self, mock_client_constructor):
mock_s3 = mock_client_constructor.return_value
mock_s3.list_objects_v2.return_value = {} # no 'Contents' key
keys = inventory.list_s3_objects('empty-bucket')
self.assertEqual(keys, [])
if __name__ == '__main__':
unittest.main()
side_effect
side_effect gives you finer control than return_value. It takes priority over return_value when both are set. There are three forms:
- An exception class or instance: the mock raises it every time it is called.
- A callable: the mock calls it with the same arguments it received and returns the result. This lets you write a real function that inspects the arguments and returns different values.
- An iterable: each successive call to the mock pops the next value from the iterable. If the value is an exception, it is raised rather than returned.
# fetcher.py
import requests
def fetch_with_retry(url, attempts=3):
for i in range(attempts):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
except requests.RequestException:
if i == attempts - 1:
raise
return None
# test_fetcher.py
import unittest
from unittest.mock import patch, MagicMock
import requests
import fetcher
class TestFetchWithRetry(unittest.TestCase):
@patch('fetcher.requests.get')
def test_raises_on_connection_error(self, mock_get):
# side_effect as an exception: every call raises ConnectionError
mock_get.side_effect = requests.ConnectionError("refused")
with self.assertRaises(requests.ConnectionError):
fetcher.fetch_with_retry('https://api.example.com/data')
# Three attempts were made before giving up
self.assertEqual(mock_get.call_count, 3)
@patch('fetcher.requests.get')
def test_succeeds_after_two_failures(self, mock_get):
# side_effect as an iterable: fail twice, succeed on the third call
ok_response = MagicMock()
ok_response.json.return_value = {'status': 'ok'}
ok_response.raise_for_status.return_value = None
mock_get.side_effect = [
requests.ConnectionError("attempt 1"),
requests.ConnectionError("attempt 2"),
ok_response,
]
result = fetcher.fetch_with_retry('https://api.example.com/data')
self.assertEqual(result, {'status': 'ok'})
self.assertEqual(mock_get.call_count, 3)
@patch('fetcher.requests.get')
def test_side_effect_as_callable(self, mock_get):
# side_effect as a callable: inspect arguments and return accordingly
def dynamic_response(url, **kwargs):
if 'timeout' not in kwargs:
raise ValueError("timeout required")
response = MagicMock()
response.json.return_value = {'url': url}
response.raise_for_status.return_value = None
return response
mock_get.side_effect = dynamic_response
result = fetcher.fetch_with_retry('https://api.example.com/items')
self.assertEqual(result['url'], 'https://api.example.com/items')
if __name__ == '__main__':
unittest.main()
Use when
You need the mock to return the same value every time it is called, regardless of arguments. The simplest and most common case.
Syntax
mock.return_value = {'status': 'ok'}
Or at creation: MagicMock(return_value=True)
Precedence
Used by default. If side_effect is also set, side_effect wins and return_value is ignored unless the side_effect callable returns DEFAULT.
Classic mistake
Forgetting that patching a class means mock.return_value is the instance — so configure methods on mock.return_value.method, not on mock.method.
Use when
You need the mock to raise an exception every time it is called — simulating a network timeout, permission error, or any failure path.
Syntax
mock.side_effect = requests.ConnectionError("refused")
Pass an exception instance or class — both work.
Why not just raise in the body?
You cannot raise inside a decorator argument. side_effect is how you declaratively wire up exception raising to a specific mock call, without touching test body flow.
Deeper use
Mix with the iterable form: put exceptions and normal return values in the same list to simulate flaky APIs — first call fails, second succeeds, third fails again.
Use when
The mock is called multiple times in sequence and each call should return (or raise) something different — retry logic, pagination, or multi-step protocols.
Syntax
mock.side_effect = [err1, err2, good_response]
Exception instances in the list are raised; all other values are returned.
When the list is exhausted
If the mock is called more times than there are entries in the list, StopIteration is raised. Always ensure your list is at least as long as the expected call count.
Deeper use
Use itertools.chain or a generator to produce an infinite or conditionally-terminated sequence — useful for long-running loop tests that pull from a queue.
Use when
The return value must depend on the arguments the mock receives at call time — dynamic routing, argument-dependent error injection, or delegating to a simplified real implementation.
Syntax
def my_side_effect(url, **kw):
return {'url': url}
mock.side_effect = my_side_effect
Return DEFAULT to fall through
If your callable returns unittest.mock.DEFAULT, the mock falls back to using return_value. This lets you conditionally intercept only some calls.
Deeper use
Assign a callable that checks a shared state dictionary — mock responses that evolve as the test progresses, simulating a stateful API where each call changes server-side data.
Assertion methods on mocks
Every mock object tracks every call made to it. The following assertion methods are available on any Mock or MagicMock instance, and they raise AssertionError on failure:
| Method | What it asserts |
|---|---|
assert_called() |
The mock was called at least once |
assert_not_called() |
The mock was never called |
assert_called_once() |
The mock was called exactly once |
assert_called_with(*args, **kwargs) |
The most recent call used these arguments |
assert_called_once_with(*args, **kwargs) |
Called exactly once with these arguments |
assert_has_calls(calls, any_order=False) |
All specified calls were made (optionally in any order) |
call_count |
Integer — total number of times the mock was called |
call_args |
The arguments of the most recent call as a call object |
call_args_list |
List of call objects for every invocation |
assert_called_once() and assert_called_once_with() are distinct methods. assert_called_once() — without arguments — was added in Python 3.6 via mock 2.0 and simply asserts that the mock was called exactly one time. A common earlier mistake was writing mock.assert_called_once() on Python versions before 3.6 and having it silently pass with no actual assertion, because plain Mock objects accept any attribute access as a new child mock. Since mock 2.0 (Python 3.5+), accessing a misspelled attribute whose name starts with assert on a non-auto-specced mock raises AttributeError when unsafe=False (the default), which surfaces many of these typos at test runtime rather than silently passing them.
mock.return_value = 'A' and mock.side_effect = ['B', 'C']. What does the first call to the mock return?autospec — making mocks enforce the real interface
By default, a MagicMock accepts any call with any arguments, because it does not know what the real object's signature looks like. This means your tests can continue to pass after you refactor the real API in a breaking way — the mock never notices. Passing autospec=True to patch tells it to build the mock by inspecting the real object's attributes and method signatures. Calls that do not match the real signature raise TypeError immediately, just as calling the real object with wrong arguments would:
# notifier.py
class AlertService:
def send(self, message: str, priority: int = 1) -> bool:
# Real implementation would call an external service
return True
# test_notifier_autospec.py
import unittest
from unittest.mock import patch
from notifier import AlertService
class TestAlertServiceAutospec(unittest.TestCase):
@patch('notifier.AlertService', autospec=True)
def test_send_called_with_correct_args(self, MockAlertService):
instance = MockAlertService.return_value
instance.send.return_value = True
service = MockAlertService()
result = service.send("Disk usage critical", priority=2)
instance.send.assert_called_once_with("Disk usage critical", priority=2)
self.assertTrue(result)
@patch('notifier.AlertService', autospec=True)
def test_wrong_argument_raises_type_error(self, MockAlertService):
instance = MockAlertService.return_value
# send() does not accept a 'severity' keyword argument.
# Without autospec this would silently pass.
# With autospec it raises TypeError immediately.
with self.assertRaises(TypeError):
instance.send("High CPU", severity=5)
if __name__ == '__main__':
unittest.main()
autospec also catches misspelled assertion method names. Without it, writing mock.assert_called_once() when you meant mock.assert_called_once_with(args) silently passes because the mock just creates a new child mock for the unknown attribute. With autospec=True, only methods that exist on the real object are accessible on the mock.
There is one known limitation of autospec: it inspects the object at patcher creation time using getattr. If your class overrides __getattribute__, creating an autospecced mock may trigger those side effects during test setup. For objects with large numbers of attributes, autospeccing adds a small but measurable overhead to test collection time. Use it where interface accuracy matters most — public APIs, third-party client classes, and any module boundary that is likely to change.
Patching async functions with AsyncMock
When the target is a coroutine function, patch automatically installs an AsyncMock rather than a MagicMock (since Python 3.8). AsyncMock is awaitable and records the same call information. You configure return_value and side_effect in exactly the same way:
# async_weather.py
import aiohttp
async def get_temperature(city: str) -> float:
async with aiohttp.ClientSession() as session:
async with session.get(f'https://api.weather.example.com/{city}') as resp:
data = await resp.json()
return data['temp_c']
# test_async_weather.py
import asyncio
import unittest
from unittest.mock import patch, AsyncMock, MagicMock
import async_weather
class TestGetTemperature(unittest.IsolatedAsyncioTestCase):
@patch('async_weather.aiohttp.ClientSession')
async def test_returns_temperature(self, MockSession):
# Build the chain of async context managers that aiohttp uses.
mock_response = MagicMock()
mock_response.json = AsyncMock(return_value={'temp_c': 18.5})
mock_request_cm = MagicMock()
mock_request_cm.__aenter__ = AsyncMock(return_value=mock_response)
mock_request_cm.__aexit__ = AsyncMock(return_value=False)
mock_session = MagicMock()
mock_session.get.return_value = mock_request_cm
mock_session_cm = MagicMock()
mock_session_cm.__aenter__ = AsyncMock(return_value=mock_session)
mock_session_cm.__aexit__ = AsyncMock(return_value=False)
MockSession.return_value = mock_session_cm
temp = await async_weather.get_temperature('London')
self.assertEqual(temp, 18.5)
if __name__ == '__main__':
unittest.main()
mock_open — patching file I/O
unittest.mock.mock_open is a helper that returns a pre-configured mock suitable for replacing Python's built-in open. It handles the context manager protocol and the read(), readline(), and iteration interfaces so you do not need to wire those up yourself:
# parser.py
def count_lines(path):
with open(path, 'r') as f:
return sum(1 for _ in f)
# test_parser.py
import unittest
from unittest.mock import patch, mock_open
import parser
FAKE_FILE_CONTENT = "line one\nline two\nline three\n"
class TestCountLines(unittest.TestCase):
def test_counts_three_lines(self):
m = mock_open(read_data=FAKE_FILE_CONTENT)
with patch('builtins.open', m):
count = parser.count_lines('/fake/path/file.txt')
self.assertEqual(count, 3)
m.assert_called_once_with('/fake/path/file.txt', 'r')
if __name__ == '__main__':
unittest.main()
Additional Parameters: wraps and new_callable
wraps — spy on a real callable without replacing it
The wraps parameter lets you install a mock that delegates every actual call to a real callable, while still recording call information. This is known as a spy: the real function executes and returns its actual result, but the mock wrapper intercepts the call so you can assert on arguments and call count afterward.
This is distinct from autospec=True. With autospec, the real function never runs; with wraps, it always does. Use wraps when you need to verify call behavior against a function whose real side effects are acceptable or necessary in the test environment:
# formatter.py
def format_currency(amount, symbol='$'):
return f"{symbol}{amount:,.2f}"
# test_formatter_spy.py
import unittest
from unittest.mock import patch
import formatter
class TestFormatCurrencySpy(unittest.TestCase):
def test_called_with_correct_args(self):
# wraps= means the real function still runs; the mock just observes.
with patch('formatter.format_currency',
wraps=formatter.format_currency) as spy:
result = formatter.format_currency(1234.5)
# The real function returned the correct string.
self.assertEqual(result, '$1,234.50')
# The spy recorded the call.
spy.assert_called_once_with(1234.5)
if __name__ == '__main__':
unittest.main()
new_callable — substituting a different mock class
By default, patch() installs a MagicMock. The new_callable parameter lets you substitute a different class. The most common reason to do this is patching a property — Python properties are descriptors, not plain attributes, and replacing them with MagicMock does not wire up the descriptor protocol. PropertyMock is the correct replacement:
# server.py
class Server:
@property
def is_online(self):
# Real implementation would check a network socket
return True
# test_server.py
import unittest
from unittest.mock import patch, PropertyMock
from server import Server
class TestServer(unittest.TestCase):
@patch.object(Server, 'is_online', new_callable=PropertyMock)
def test_offline_branch(self, mock_is_online):
mock_is_online.return_value = False
s = Server()
self.assertFalse(s.is_online)
mock_is_online.assert_called_once()
if __name__ == '__main__':
unittest.main()
Since Python 3.8, patch() automatically installs an AsyncMock when the target is detected as a coroutine function, so new_callable=AsyncMock is rarely needed for async targets on 3.8+. You would only specify it explicitly on older codebases or when patching a non-coroutine that you want to behave as one for testing purposes.
patch.multiple — patching several targets in one call
patch.multiple() accepts a target module and any number of keyword arguments, each naming an attribute to patch in that module. Unlike stacking individual @patch decorators, patch.multiple does not inject mock arguments into the test method unless you pass DEFAULT as the value. This makes it useful for patching several side-effect-prone attributes where you need them replaced but do not need to assert on them individually:
# test_multiple.py
import unittest
from unittest.mock import patch, DEFAULT, MagicMock
import os
class TestMultiplePatch(unittest.TestCase):
# Patch os.getcwd and os.listdir in one decorator call.
# Passing DEFAULT tells patch.multiple to create a MagicMock and
# inject it as a keyword argument named after the attribute.
@patch.multiple('os', getcwd=DEFAULT, listdir=DEFAULT)
def test_directory_listing(self, getcwd, listdir):
getcwd.return_value = '/tmp/project'
listdir.return_value = ['README.md', 'main.py']
cwd = os.getcwd()
files = os.listdir(cwd)
self.assertEqual(cwd, '/tmp/project')
self.assertIn('main.py', files)
getcwd.assert_called_once()
if __name__ == '__main__':
unittest.main()
Key Takeaways
- Patch where the name is looked up, not where it is defined. The target string must point to the attribute in the namespace that the code under test will access at runtime. A
from x import yimport creates a local binding in the importing module; patch that binding, notx.yin the original module. - Use
autospec=Trueto protect against interface drift. A plainMagicMockaccepts any call regardless of argument count or attribute name.autospec=Truebuilds the mock from the real object's signature so that breaking refactors are caught by tests rather than silently passing. - Prefer
side_effectfor exception simulation and sequential return values. Setside_effectto an exception class to simulate failures, to an iterable to return different values on successive calls, or to a callable when the response depends on the arguments received.side_effecttakes precedence overreturn_valuewhen both are set. - Use
wrapswhen you need real behavior plus call observability. Unlike replacing a function entirely,wrapsdelegates to the real callable while the mock records the interaction — a targeted spy that does not change program output. - Always register cleanup with
addCleanupwhen starting patchers manually. When usingpatcher.start()insetUp, callself.addCleanup(patcher.stop)rather than relying ontearDown. Cleanup functions run even whensetUpraises midway, preventing leaked patches from contaminating subsequent tests.
Controlled isolation through @unittest.mock.patch is what separates a test that verifies your logic from a test that verifies your infrastructure. Once you internalize the lookup rule, the decorator stacking order, and the return_value/side_effect distinction, the entire module becomes a straightforward and powerful tool for writing deterministic, fast, and dependency-free unit tests.
Frequently Asked Questions
What does @unittest.mock.patch actually replace?
@unittest.mock.patch temporarily replaces a named attribute in a module's namespace with a MagicMock instance, or another object you specify via the new argument. According to the Python standard library documentation, the replacement is restored once the test ends — even when an exception is raised. The mechanism is straightforward: patch uses getattr to save the original value, setattr to install the replacement, and setattr again at cleanup to restore it.
Why do I need to patch where the object is used, not where it is defined?
When a module imports a name with from module import name, it creates a local binding to that name in its own namespace. Patching the source module replaces the object there, but the local binding in the module under test already holds a direct reference to the original object — patching the source has no effect on it. The Python documentation makes this a first-class rule: the correct namespace to target is the one the code under test resolves names from at runtime, not where those names were originally defined. See: Python docs — unittest.mock quick guide.
What is the difference between patch(), patch.object(), and patch.dict()?
patch() takes a dotted string path and patches the named attribute in that module namespace. patch.object() takes a live Python object and a string attribute name — it is useful when you already have a reference to the object and want your test to fail at load time if the attribute does not exist, rather than at runtime. patch.dict() patches a dictionary for the duration of the test, merging in new keys and restoring the original state on exit; it is the standard approach for controlling os.environ without touching it directly.
When should I use autospec=True?
Use autospec=True whenever you want the mock to enforce the real object's method signatures and attribute interface. Without it, a plain MagicMock accepts any call regardless of argument count or attribute name, which means tests can pass silently after a breaking API refactor. The Python documentation confirms that auto-speccing creates mocks that have the same attributes and methods as the objects they replace, and that any functions and methods have the same call signature as the real object — so your mocks will fail in the same way your production code would if used incorrectly.
What is the difference between return_value and side_effect?
return_value sets a fixed value returned every time the mock is called. side_effect is more flexible: it can be an exception class or instance to raise on call, an iterable that yields successive values (raising any exception objects in the sequence rather than returning them), or a callable that receives the same arguments and computes a response. When both are set, the Python documentation confirms that side_effect takes precedence over return_value.
How do I patch multiple objects in a single test?
Stack multiple @patch decorators above the test method. Decorators are applied bottom-up, so the decorator closest to the def line corresponds to the first extra parameter in the method signature; the topmost decorator corresponds to the last. Alternatively, use patch.multiple() to patch several attributes of the same module in one decorator call, passing DEFAULT as the value for each attribute you want injected as a keyword argument.
What does the wraps parameter do?
The wraps parameter installs a mock that delegates every call to a real callable, while still recording it. The real function runs and returns its actual result; the mock wrapper intercepts the call so you can assert on arguments and call count afterward. This is a spy pattern: use it when you need to verify that a real function was called correctly without suppressing its side effects. It is the opposite of autospec, where the real function never runs.
What is new_callable used for in patch()?
new_callable lets you substitute a different mock class instead of the default MagicMock. The most common use is patching a property — Python properties are descriptors, and a plain MagicMock does not wire up the descriptor protocol correctly. Using new_callable=PropertyMock with patch.object() produces a mock that behaves as a property. Since Python 3.8, patch() automatically uses AsyncMock for coroutine function targets, so explicit new_callable=AsyncMock is rarely needed on modern Python.
Sources and References
- Python Software Foundation. unittest.mock — mock object library. Python 3 Standard Library Documentation. Retrieved March 2026.
- Python Software Foundation. unittest.mock — getting started. Python 3 Standard Library Documentation. Retrieved March 2026.
- Python Software Foundation. What's New in Python 3.8. Python 3 Documentation. AsyncMock and IsolatedAsyncioTestCase added in 3.8.
- PyPI. mock — Rolling backport of unittest.mock. mock 5.2.0. March 2025.