Зачем нужны тест-дублёры
Зачем нужны тест-дублёры: как изолировать код от базы данных, сети, времени и случайности.
Тест-дублёр (mock) — поддельный объект, который подставляют вместо реальной зависимости (БД, API, файла), чтобы тест был быстрым, изолированным и предсказуемым.
Проблема: зависимости мешают тестировать
Юнит-тест должен проверять одну единицу кода в изоляции. Но функция часто обращается наружу: ходит в базу, дёргает чужой API, читает файл, смотрит на текущее время или генерирует случайное число. Такие зависимости ломают тест:
- Медленно. Сетевой запрос — это сотни миллисекунд против микросекунд для чистой функции.
- Ненадёжно. Сервер может быть недоступен, и тест упадёт не из-за вашего кода.
- Недетерминированно.
datetime.now()иrandomкаждый раз дают разное — нечего сравнивать. - С побочными эффектами. Тест не должен реально списывать деньги или слать письма.
Идея: подменить зависимость заглушкой
Вместо настоящей базы подставляем объект, который притворяется базой и возвращает заранее заданные данные. Тогда мы тестируем свою логику, а не работоспособность чужого сервиса. Это и есть мокинг.
Покажем идею вручную, без библиотек, чтобы понять суть. Функция считает итог заказа, обращаясь к «репозиторию цен». В тесте подсунем поддельный репозиторий:
def order_total(price_repo, item_ids):
return sum(price_repo.get_price(i) for i in item_ids)
# В реальности price_repo ходил бы в БД. В тесте — заглушка.
class FakePriceRepo:
def get_price(self, item_id):
prices = {1: 100, 2: 250, 3: 50}
return prices[item_id]
fake = FakePriceRepo()
result = order_total(fake, [1, 2, 3])
print("Итог заказа:", result)
assert result == 400
print("Тест прошёл без всякой базы данных")
Вывод:
Итог заказа: 400 Тест прошёл без всякой базы данных
Никакой настоящей БД — тест мгновенный и стабильный. Мы проверили именно order_total, а не доступность базы. Это и есть изоляция.
Виды тест-дублёров (терминология)
| Тип | Назначение |
| Stub (заглушка) | возвращает заранее заданные данные |
| Mock | заглушка + проверка, что его вызвали правильно |
| Fake | упрощённая рабочая реализация (например, словарь вместо БД) |
| Spy | оборачивает реальный объект и записывает вызовы |
На практике в Python границы размыты, и почти всё делают через один инструмент — unittest.mock, которым займёмся в следующем уроке. Главное — понимать зачем: убрать из теста медленные, ненадёжные и недетерминированные зависимости.
Когда мок не нужен
Не мокайте то, что и так быстрое и чистое. Если функция работает только с числами и списками — мок только усложнит тест. Мок оправдан, когда зависимость медленная, внешняя или непредсказуемая: сеть, БД, файловая система, время, случайность, отправка писем/платежей.
Опасность переусердствовать с моками
Мокинг — мощный инструмент, но им легко злоупотребить. Если замокать вообще всё, тест начнёт проверять не реальное поведение, а ваши предположения о том, как зависимость себя ведёт. Классический симптом: вы меняете код, он явно сломан, а тесты зелёные — потому что моки «знают» только старое поведение. Поэтому мокайте границы системы (внешние сервисы, БД, сеть), но не мокайте внутренние объекты, которые и так быстро и детерминированно работают. Тест должен проверять настоящую логику, а заглушки ставить лишь там, где без них нельзя.
Изоляция и архитектура связаны
Если код тяжело замокать, это обычно сигнал о проблеме в самом коде, а не в тесте. Функция, которая внутри себя создаёт соединение с БД и тут же его использует, плохо тестируется — зависимость некуда подставить. А функция, которая принимает соединение аргументом, тестируется тривиально: передал мок и проверяй. Так тестирование подталкивает к более гибкой архитектуре: зависимости передают снаружи, а не «зашивают» внутрь. Это побочная польза тестов — они улучшают дизайн кода.
Итог
- Тест-дублёры изолируют код от БД, сети, файлов, времени и случайности.
- Без них тесты медленные, ненадёжные и недетерминированные.
- Идея проста: подсунуть поддельный объект вместо настоящей зависимости.
- Мокать стоит внешнее и непредсказуемое, а не чистую логику.