Home Python Mocking in Python: unittest.mock and pytest-mock Guide
Intermediate 3 min · July 14, 2026

Mocking in Python: unittest.mock and pytest-mock Guide

Learn mocking in Python with unittest.mock and pytest-mock.

N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Notes here come from systems that actually shipped.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic Python knowledge
  • Familiarity with unit testing concepts
  • Python 3.6+ installed
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Mocking replaces real objects with fake ones to isolate code during testing.
  • Use unittest.mock.Mock and patch for flexible mocking.
  • pytest-mock provides a cleaner mocker fixture for pytest.
  • Control return values, side effects, and assert calls.
  • Avoid common pitfalls like mocking wrong paths or forgetting autospec.
✦ Definition~90s read
What is Mocking in Python?

Mocking is a technique to replace real objects with simulated ones that mimic their interface, allowing you to test code in isolation.

Think of mocking like using a flight simulator to train a pilot.
Plain-English First

Think of mocking like using a flight simulator to train a pilot. You don't need a real plane; you simulate the controls and responses. In Python, mocking lets you test your code by replacing external services (like databases or APIs) with pretend versions that behave exactly as you want.

When writing unit tests, you want to test a single unit of code in isolation. But real-world code often depends on external systems: databases, APIs, file systems, or even the current time. If you test against the real thing, your tests become slow, unreliable, and dependent on infrastructure. That's where mocking comes in.

Mocking allows you to replace real objects with fake ones that mimic their behavior. You can control what they return, what side effects they have, and later assert that they were called correctly. Python's standard library provides unittest.mock, and the pytest ecosystem offers pytest-mock for a more seamless experience.

In this tutorial, you'll learn how to use both libraries effectively. We'll cover creating mock objects, patching imports, setting return values and side effects, and advanced techniques like spies and autospec. You'll also see real-world production incidents where mocking saved the day—or where improper mocking caused bugs.

By the end, you'll be able to write robust, isolated tests that run fast and give you confidence in your code. Let's dive in.

Setting Up Mock Objects with unittest.mock

The unittest.mock module provides the Mock class, which can simulate any object. You can create a mock and set its return value or side effects.

Let's start with a simple example: mocking a function that fetches data from an API.

mock_basics.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
from unittest.mock import Mock

def fetch_data(api_client):
    return api_client.get('/data')

# Create a mock client
mock_client = Mock()
mock_client.get.return_value = {'key': 'value'}

result = fetch_data(mock_client)
print(result)
print(mock_client.get.called)
print(mock_client.get.call_args)
Output
{'key': 'value'}
True
call('/data')
💡Mock attributes are created on the fly
📊 Production Insight
In production, never mock what you don't own. Use integration tests for external services.
🎯 Key Takeaway
Mock objects record every call, allowing you to assert behavior after the fact.

Patching with unittest.mock.patch

patch temporarily replaces a target with a mock during a test. It can be used as a decorator or context manager. The target is a string in the form 'package.module.ClassName'. It's crucial to patch where the object is used, not where it's defined.

Example: patching datetime.datetime.now to return a fixed date.

patch_example.pyPYTHON
1
2
3
4
5
6
7
8
9
from unittest.mock import patch
from datetime import datetime

def get_timestamp():
    return datetime.now().isoformat()

with patch('datetime.datetime') as mock_dt:
    mock_dt.now.return_value = datetime(2025, 1, 1)
    print(get_timestamp())
Output
2025-01-01T00:00:00
⚠ Patch the right path
🎯 Key Takeaway
Use patch to replace objects in the module under test's namespace.

Using pytest-mock for Cleaner Tests

pytest-mock provides a mocker fixture that wraps unittest.mock and integrates seamlessly with pytest. It automatically cleans up after the test.

Example: testing a function that sends an email.

test_email.pyPYTHON
1
2
3
4
5
6
7
8
9
10
import pytest
from myapp import send_welcome_email

def test_send_welcome_email(mocker):
    mock_smtp = mocker.patch('myapp.smtplib.SMTP')
    mock_smtp_instance = mock_smtp.return_value
    send_welcome_email('user@example.com')
    mock_smtp_instance.sendmail.assert_called_once()
    args, _ = mock_smtp_instance.sendmail.call_args
    assert 'Welcome' in args[2]
🔥mocker.patch returns the mock
🎯 Key Takeaway
pytest-mock simplifies mocking in pytest by providing a clean fixture.

Controlling Return Values and Side Effects

Mocks can return fixed values, raise exceptions, or yield multiple values on successive calls. Use return_value for a single return, and side_effect for more complex behavior.

Example: simulating a database that returns different results on each call.

side_effect.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from unittest.mock import Mock

def get_users(db):
    return [db.fetch_one(), db.fetch_one()]

mock_db = Mock()
mock_db.fetch_one.side_effect = [{'id': 1}, {'id': 2}]

users = get_users(mock_db)
print(users)

# Raise an exception on third call
mock_db.fetch_one.side_effect = [{'id': 1}, {'id': 2}, Exception('DB down')]
try:
    get_users(mock_db)
except Exception as e:
    print(e)
Output
[{'id': 1}, {'id': 2}]
DB down
💡side_effect can be a callable
📊 Production Insight
Always test error paths by raising exceptions via side_effect.
🎯 Key Takeaway
Use side_effect to simulate sequences, exceptions, or dynamic behavior.

Asserting Calls and Using Spies

After a test, you can assert that mocks were called with specific arguments, call counts, and order. Spies wrap a real object and record calls without replacing behavior.

Example: using wraps to create a spy.

spy_example.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
from unittest.mock import Mock

def original(x):
    return x * 2

mock = Mock(wraps=original)
print(mock(5))
print(mock(3))
mock.assert_any_call(5)
mock.assert_any_call(3)
print(mock.call_count)
Output
10
6
2
🔥Spies preserve original behavior
🎯 Key Takeaway
Assertions like assert_called_with and assert_any_call verify that your code interacts correctly with dependencies.

Autospec and Spec: Preventing Mock Drift

When you create a mock without a spec, it accepts any attribute or method call. This can hide bugs if the real API changes. autospec automatically creates a mock that matches the interface of the original object.

Example: using autospec with patch.

autospec_example.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
from unittest.mock import patch

class MyAPI:
    def fetch(self, id):
        return {'id': id}

# Without autospec, this passes even though method name is wrong
with patch('__main__.MyAPI', autospec=True) as MockAPI:
    api = MockAPI()
    # api.fetech(1)  # Would raise AttributeError because 'fetech' doesn't exist
    api.fetch(1)  # Works
    MockAPI.assert_called_once()
⚠ Always use autospec=True
📊 Production Insight
In the payment incident, autospec would have caught the missing header parameter.
🎯 Key Takeaway
Autospec ensures your mock matches the real object's interface, catching mismatches early.
● Production incidentPOST-MORTEMseverity: high

The Case of the Silent Payment Failure

Symptom
Users reported payments failing with no error message.
Assumption
The payment gateway integration was thoroughly tested with mocks.
Root cause
The mock was too permissive: it accepted any arguments and returned success, but the real gateway required a specific header.
Fix
Replaced the mock with an autospec mock that enforced the real interface, and added integration tests.
Key lesson
  • Always use autospec=True to match the real object's interface.
  • Don't mock everything; write integration tests for critical paths.
  • Review mock setups during code reviews.
  • Use side_effect to simulate realistic failures.
  • Log actual vs expected calls in tests for debugging.
Production debug guideSymptom to Action3 entries
Symptom · 01
Test passes but production fails
Fix
Check if mock is too permissive; add autospec and verify call signatures.
Symptom · 02
Mock not being called
Fix
Verify the patch target path matches the import location in the module under test.
Symptom · 03
Unexpected side effects
Fix
Review side_effect functions and ensure they raise correct exceptions.
★ Quick Debug Cheat SheetCommon mocking symptoms and immediate fixes.
Mock not patching
Immediate action
Check patch path
Commands
print(where_used)
patch('module.ClassName')
Fix now
Use patch.object if patching an attribute.
Wrong return value+
Immediate action
Set return_value
Commands
mock.return_value = 42
mock.side_effect = [1,2,3]
Fix now
Use side_effect for sequences.
AssertionError on call count+
Immediate action
Check actual calls
Commands
print(mock.call_args_list)
mock.assert_called_once_with(...)
Fix now
Use assert_any_call for partial matches.
Featureunittest.mockpytest-mock
SetupRequires manual cleanupAutomatic cleanup via fixture
Patchingpatch() decorator/context managermocker.patch() fixture
Async supportAsyncMock classmocker.patch returns AsyncMock automatically
IntegrationPart of standard libraryThird-party plugin for pytest
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
mock_basics.pyfrom unittest.mock import MockSetting Up Mock Objects with unittest.mock
patch_example.pyfrom unittest.mock import patchPatching with unittest.mock.patch
test_email.pyfrom myapp import send_welcome_emailUsing pytest-mock for Cleaner Tests
side_effect.pyfrom unittest.mock import MockControlling Return Values and Side Effects
spy_example.pyfrom unittest.mock import MockAsserting Calls and Using Spies
autospec_example.pyfrom unittest.mock import patchAutospec and Spec

Key takeaways

1
Mocking isolates your code by replacing dependencies with controllable fakes.
2
Use unittest.mock.patch or pytest-mock's mocker fixture for clean patching.
3
Always use autospec=True to prevent mock drift.
4
Leverage side_effect for dynamic responses and error simulation.
5
Write integration tests for critical paths to complement unit tests.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the difference between mock, stub, and fake.
Q02SENIOR
How would you mock a database connection in a unit test?
Q03SENIOR
What is autospec and why is it important?
Q01 of 03SENIOR

Explain the difference between mock, stub, and fake.

ANSWER
A mock is used for behavior verification (asserting calls). A stub provides canned answers. A fake has working implementation but simplified (e.g., in-memory database).
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between Mock and MagicMock?
02
How do I mock an entire module?
03
Can I mock async functions?
04
Why does my patch not work?
05
How do I mock environment variables?
N
Naren Founder & Principal Engineer

20+ years shipping production Python across data and backend systems. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Advanced Python. Mark it forged?

3 min read · try the examples if you haven't

Previous
Advanced pytest: Parametrization, Mocking, and Fixture Patterns
21 / 35 · Advanced Python
Next
Python Project Management: Poetry, uv, and Rye