Skip to content

Mocking Dependencies with unittest.mock

pytest does not ship its own mocking library, but it works extremely well with unittest.mock, which is part of the Python standard library. This allows you to replace external dependencies, network calls, or expensive operations with controlled mock objects. Mock objects are sometimes called test doubles, spies, fakes or stubs. By combining pytest’s monkeypatch fixture with the unittest.mock module, you can cover nearly all common test double use cases.

A common pattern when using unittest.mock is to rely on patch, either as a decorator or as a context manager:

python
from unittest.mock import patch

def fetch_data():
    return "real data"

def test_fetch_data():
    with patch(__name__ + ".fetch_data", return_value="mocked data"):
        assert fetch_data() == "mocked data"

In this example, patch temporarily replaces the fetch_data function in the current module with a mock object for the duration of the with block. Any call to fetch_data() inside this block does not execute the original function. Instead, it returns the value specified by return_value. Once the block exits, the original function is automatically restored. This scoping behavior ensures that mocks do not leak into other tests.

In addition to patch, unittest.mock provides the MagicMock class, which is a flexible mock object that can dynamically handle attribute access, method calls, and return values without requiring explicit definitions.

python
from unittest.mock import MagicMock

def process(service):
    return service.run()

def test_process_with_magicmock():
    service = MagicMock()
    service.run.return_value = "mocked result"

    result = process(service)

    assert result == "mocked result"
    service.run.assert_called_once() # verifies that run() was invoked exactly once

Here, MagicMock is used to stand in for a dependency object. The run method does not exist on a real class, but MagicMock allows it to be accessed and configured anyway. The assertion service.run.assert_called_once() verifies that this method was invoked exactly once, ensuring that the function under test interacts with its dependency as expected.

While unittest.mock works well with pytest, many teams prefer using the pytest-mock plugin, which provides a thin and convenient wrapper around unittest.mock. The plugin introduces a built-in mocker fixture that simplifies mock creation and automatically handles cleanup between tests.

Using pytest-mock, the earlier patch example can be rewritten in a more pytest-native style:

python
def fetch_data():
    return "real data"

def test_fetch_data(mocker):
    mocker.patch(__name__ + ".fetch_data", return_value="mocked data")
    assert fetch_data() == "mocked data"

Similarly, MagicMock can be used as mocker.MagicMock, without changing rest of the code in the example.