Зачем нужны тест-дублёры

Зачем нужны тест-дублёры: как изолировать код от базы данных, сети, времени и случайности.

Тест-дублёр (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, которым займёмся в следующем уроке. Главное — понимать зачем: убрать из теста медленные, ненадёжные и недетерминированные зависимости.

Когда мок не нужен

Не мокайте то, что и так быстрое и чистое. Если функция работает только с числами и списками — мок только усложнит тест. Мок оправдан, когда зависимость медленная, внешняя или непредсказуемая: сеть, БД, файловая система, время, случайность, отправка писем/платежей.

Опасность переусердствовать с моками

Мокинг — мощный инструмент, но им легко злоупотребить. Если замокать вообще всё, тест начнёт проверять не реальное поведение, а ваши предположения о том, как зависимость себя ведёт. Классический симптом: вы меняете код, он явно сломан, а тесты зелёные — потому что моки «знают» только старое поведение. Поэтому мокайте границы системы (внешние сервисы, БД, сеть), но не мокайте внутренние объекты, которые и так быстро и детерминированно работают. Тест должен проверять настоящую логику, а заглушки ставить лишь там, где без них нельзя.

Изоляция и архитектура связаны

Если код тяжело замокать, это обычно сигнал о проблеме в самом коде, а не в тесте. Функция, которая внутри себя создаёт соединение с БД и тут же его использует, плохо тестируется — зависимость некуда подставить. А функция, которая принимает соединение аргументом, тестируется тривиально: передал мок и проверяй. Так тестирование подталкивает к более гибкой архитектуре: зависимости передают снаружи, а не «зашивают» внутрь. Это побочная польза тестов — они улучшают дизайн кода.

Итог

  • Тест-дублёры изолируют код от БД, сети, файлов, времени и случайности.
  • Без них тесты медленные, ненадёжные и недетерминированные.
  • Идея проста: подсунуть поддельный объект вместо настоящей зависимости.
  • Мокать стоит внешнее и непредсказуемое, а не чистую логику.
Проверьте себя
1. Зачем мокать зависимости в юнит-тестах?
AЧтобы тесты были длиннее
BЧтобы изолировать код от медленных/ненадёжных/недетерминированных зависимостей (БД, сеть, время)
CЧтобы увеличить покрытие
DЭто требование Python
2. Что НЕ стоит мокать?
AОбращение к сети
BЗапрос в БД
CЧистую логику с числами и списками
DТекущее время
3. Что такое stub (заглушка)?
AТест, который всегда падает
BДублёр, возвращающий заранее заданные данные
CРеальная база данных
DОтчёт о покрытии
Поддержать проект