Mocking in Python: unittest.mock and pytest-mock Guide
Learn mocking in Python with unittest.mock and pytest-mock.
20+ years shipping production Python across data and backend systems. Notes here come from systems that actually shipped.
- ✓Basic Python knowledge
- ✓Familiarity with unit testing concepts
- ✓Python 3.6+ installed
- Mocking replaces real objects with fake ones to isolate code during testing.
- Use
unittest.mock.Mockandpatchfor flexible mocking. pytest-mockprovides a cleanermockerfixture for pytest.- Control return values, side effects, and assert calls.
- Avoid common pitfalls like mocking wrong paths or forgetting autospec.
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.
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 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.
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 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.
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.
The Case of the Silent Payment Failure
- 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.
print(where_used)patch('module.ClassName')| File | Command / Code | Purpose |
|---|---|---|
| mock_basics.py | from unittest.mock import Mock | Setting Up Mock Objects with unittest.mock |
| patch_example.py | from unittest.mock import patch | Patching with unittest.mock.patch |
| test_email.py | from myapp import send_welcome_email | Using pytest-mock for Cleaner Tests |
| side_effect.py | from unittest.mock import Mock | Controlling Return Values and Side Effects |
| spy_example.py | from unittest.mock import Mock | Asserting Calls and Using Spies |
| autospec_example.py | from unittest.mock import patch | Autospec and Spec |
Key takeaways
unittest.mock.patch or pytest-mock's mocker fixture for clean patching.autospec=True to prevent mock drift.side_effect for dynamic responses and error simulation.Interview Questions on This Topic
Explain the difference between mock, stub, and fake.
Frequently Asked Questions
20+ years shipping production Python across data and backend systems. Notes here come from systems that actually shipped.
That's Advanced Python. Mark it forged?
3 min read · try the examples if you haven't