Monkeypatch vs Mock in pytest: Key Differences and Usage
pytest, monkeypatch directly replaces attributes or functions at runtime, while mock creates mock objects to simulate and control behavior. monkeypatch is simpler for quick replacements, and mock offers more control and inspection of calls.Quick Comparison
This table summarizes the main differences between monkeypatch and mock in pytest.
| Factor | monkeypatch | mock |
|---|---|---|
| Purpose | Replace attributes or functions at runtime | Create mock objects to simulate behavior |
| Control | Simple direct replacement | Detailed control over calls and return values |
| Inspection | No built-in call tracking | Tracks calls, arguments, and call count |
| Setup | Uses pytest fixture monkeypatch | Use unittest.mock or pytest-mock plugin |
| Use case | Quick patching of simple functions or variables | Complex mocking with assertions on usage |
| Cleanup | Automatic after test ends | Automatic with context managers or decorators |
Key Differences
monkeypatch is a pytest fixture that lets you replace or delete attributes, dictionary items, or environment variables temporarily during a test. It works by directly changing the target object at runtime, which makes it very straightforward for simple patches like swapping a function or changing a global variable.
On the other hand, mock (from Python's unittest.mock module) creates mock objects that simulate real objects. These mocks can record how they were used, what arguments were passed, and how many times they were called. This makes mock powerful for verifying interactions and controlling complex behavior.
While monkeypatch is great for quick and simple replacements, mock is better when you need detailed control and assertions on how the mocked parts are used. Both clean up automatically after tests, but mock often requires explicit setup via decorators or context managers, whereas monkeypatch is provided as a fixture by pytest.
Code Comparison
def get_data(): return 'real data' def process(): return get_data().upper() def test_process_with_monkeypatch(monkeypatch): def fake_get_data(): return 'fake data' monkeypatch.setattr(__name__, 'get_data', fake_get_data) result = process() assert result == 'FAKE DATA'
Mock Equivalent
from unittest import mock def get_data(): return 'real data' def process(): return get_data().upper() def test_process_with_mock(): with mock.patch(__name__ + '.get_data', return_value='fake data') as mocked_get: result = process() mocked_get.assert_called_once() assert result == 'FAKE DATA'
When to Use Which
Choose monkeypatch when you want a quick and simple way to replace functions, variables, or environment settings without needing to track calls or assert usage details. It's perfect for straightforward patches in pytest tests.
Choose mock when you need detailed control over the mocked object's behavior, want to assert how many times it was called, with what arguments, or simulate complex interactions. It's ideal for verifying interactions and behavior in more complex test scenarios.
Key Takeaways
monkeypatch replaces attributes simply and is built into pytest as a fixture.mock creates mock objects with detailed call tracking and control.monkeypatch for quick, simple patches and mock for complex behavior verification.mock) or just replacement (monkeypatch).