# Expensive mock setup (unittest.mock, autospec)

> mock.patch with autospec=True, create_autospec and deep MagicMock chains cost real time when repeated in every test. Patch once per class or module and reuse spec'd mocks.

Mocks feel free, but they aren't. Every `Mock` is a Python object with its own class, every attribute you touch on it creates another mock, and `autospec=True` inspects the signature of every method on the object it copies. One patch in one test costs microseconds to milliseconds. The same patch in the `setUp` of a large class, inside a loop, or on a big class with autospec, repeated across thousands of tests, adds up to seconds.

## Why mocks get expensive

- **Autospec.** `mock.patch(..., autospec=True)` and `create_autospec(SomeClass)` go through `dir()` of the spec, fetch every attribute (properties run), and for every method create a child `MagicMock` with its own return-value mock and a copy of the method's signature. A class with hundreds of methods, like a Django model, an ORM session or an SDK client, makes that hundreds of mocks, built again every time the patch starts.
- **A new class per mock.** `unittest.mock` gives every mock instance its own subclass, so it can attach magic methods without affecting other mocks. Creating thousands of mocks creates thousands of classes.
- **Deep attribute chains.** `client.orders.list.return_value.data.items.return_value = [...]` creates a mock for every step.
- **Patching in loops and per test.** A patch inside a `for` loop or a parametrized test starts and stops every iteration; a dozen stacked `@mock.patch` decorators do all of that for every test.

## Patch once per class or module

When every test in a class or module needs the same patch, start it once.

### unittest / Django TestCase: setUpClass

A class-level `@mock.patch(...)` decorator doesn't help here: it wraps each test method separately, so the patch still starts and stops once per test. Start the patcher in `setUpClass` instead:

```python
from unittest import mock

from django.test import TestCase
from model_bakery import baker

from shop.checkout import checkout
from shop.models import Order


class CheckoutTests(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        patcher = mock.patch("shop.checkout.PaymentGateway", autospec=True)
        cls.gateway_class = patcher.start()
        cls.addClassCleanup(patcher.stop)

    @classmethod
    def setUpTestData(cls):
        cls.order = baker.make(Order, total=100)

    def setUp(self):
        self.gateway_class.reset_mock()

    def test_charges_the_order_total(self):
        checkout(self.order)
        self.gateway_class.return_value.charge.assert_called_once_with(amount=100)
```

The autospec is built once for the class. `reset_mock()` clears the recorded calls between tests (on the child mocks too), but keeps configured return values and side effects. If a test sets `side_effect`, undo it in that test or in `setUp`. Avoid `reset_mock(return_value=True)` on an autospecced class: it throws away the spec'd instance mock along with the return value.

### pytest: a module-scoped fixture

The `monkeypatch` and `mocker` fixtures are function-scoped. For a wider scope use `pytest.MonkeyPatch.context()`, or pytest-mock's `module_mocker` / `class_mocker` / `session_mocker`:

```python
import pytest


@pytest.fixture(scope="module")
def gateway_class(module_mocker):
    return module_mocker.patch("shop.checkout.PaymentGateway", autospec=True)


@pytest.fixture
def gateway(gateway_class):
    gateway_class.reset_mock()
    gateway = gateway_class.return_value
    yield gateway
    gateway.charge.side_effect = None
```

Tests take `gateway` and get a mock with no recorded calls, while the expensive patch runs once per module. Reset whatever your tests configure (here, a `side_effect` on `charge`) at the end of the fixture.

## Reuse spec'd mocks

When code receives a dependency as an argument, you don't need to patch at all, but `create_autospec` per test still costs the same. Build the spec'd mock once and reset it, as above. For a spec that's only used to catch typos in attribute names, `mock.Mock(spec=SomeClass)` is cheaper than `create_autospec`: it restricts attributes to the names in `dir(SomeClass)` without building a child mock and a signature check for every method up front. Use `autospec` where signatures matter (catching a call with the wrong arguments), not by default everywhere.

## Simplify what you mock

- Configure chains once: `orders = client.orders.list.return_value` then set attributes on `orders`, instead of repeating the full chain.
- Use a real object or a small fake class instead of a deeply configured `MagicMock`. A dataclass with three fields is faster and easier to read than a mock with three configured attributes.
- Move patches out of loops: patch once around the loop, and change `return_value` or `side_effect` inside it.
- Patch at the transport, not every function: for HTTP, one `responses` or `respx` mock replaces a stack of patched client functions. See [mock and block real network calls](https://flakytest.dev/guides/block-network-in-tests/).

## Keep the tests correct

- A mock shared across a class or module records calls from every test. Reset it before each test (`reset_mock()`), and undo any return values or side effects a test configures, or assertions see other tests' calls and setups.
- Never let a wider-scoped patch leak: stop it with `addClassCleanup`, or use a fixture that undoes it at the end of its scope. Run the suite in random order (pytest-randomly) to catch leaks.
- Replacing `autospec` with a plain `Mock` loses signature checking: a call with wrong arguments passes. Keep `autospec` on the boundaries where that matters.

## How flakytest finds it

flakytest puts time spent in `unittest.mock`, pytest-mock, freezegun, time-machine, responses and respx in the *mocking* category. When mocking (not counting freezegun, which has [its own suggestion](https://flakytest.dev/guides/freezegun-to-time-machine/)) takes more than a second and 3% of test time, it suggests **Mock setup is expensive**. If more than 0.2 seconds of it is in autospec (`create_autospec` and signature checks), the summary says how much. The suggestion lists the tests and the lines of your code that create the most expensive mocks, so you can see which patch to move up a level.

## Further reading

- [Python: unittest.mock, autospeccing](https://docs.python.org/3/library/unittest.mock.html#autospeccing)
- [Python: patch methods start and stop](https://docs.python.org/3/library/unittest.mock.html#patch-methods-start-and-stop)
- [pytest-mock: usage](https://pytest-mock.readthedocs.io/en/latest/usage.html)
- [pytest: monkeypatch](https://docs.pytest.org/en/stable/how-to/monkeypatch.html)
