Перейти к содержимому

Mocking и Stubbing

Mock — проверяет взаимодействие («вызвали ли метод с правильными параметрами?»). Stub — возвращает предопределённый ответ («всегда отвечай успехом»). Используются, чтобы тестировать компонент изолированно, без реальных БД, API, очередей. Fake — упрощённая рабочая реализация (например, in-memory БД вместо PostgreSQL). WireMock — заглушка для HTTP API (симуляция ответов, ошибок, задержек). Риск избыточного мокинга: тесты зелёные, а реальная интеграция сломана. Риск недостаточного: тесты медленные и нестабильные.

Введение: Дублёры в тестировании

Представьте, что вы тестируете систему управления заказами, которая отправляет уведомления в SMS-шлюз. Каждый раз, когда вы запускаете тест, он отправляет реальное SMS. Это дорого, медленно и может разозлить получателей.

Вместо этого вы подменяете SMS-шлюз заглушкой (stub), которая всегда отвечает “успешно”. Или двойником (mock), который запоминает, что его вызывали, и потом вы проверяете: “А точно ли SMS был отправлен?”.

Mocking и Stubbing — это техники создания дублёров для зависимостей при тестировании. Они позволяют тестировать компонент в изоляции, не поднимая реальные базы данных, внешние API, очереди сообщений.

Для системного аналитика эти понятия важны, потому что они определяют, насколько тесты надёжны, быстры и независимы. Вы не пишете эти тесты, но вы оцениваете стратегию тестирования, интерпретируете результаты и понимаете ограничения.

Основные понятия

ПонятиеЧто делаетВопрос, на который отвечает
Stub (заглушка)Возвращает предопределённый ответ“Что вернёт зависимость?”
Mock (двойник)Запоминает вызовы и проверяет их“Вызвали ли зависимость правильно?”
Fake (фейк)Упрощённая рабочая реализация“Как зависимость работает?”
Spy (шпион)Обёртка вокруг реального объекта“Что реально происходило?”

Stub (Заглушка)

Что это

Stub — это объект, который возвращает заранее заданные ответы на вызовы. Он не проверяет, как его вызвали. Просто возвращает то, что вы настроили.

Когда использовать

СитуацияПример
Тест не проверяет взаимодействиеПроверяем только расчёт цены, не проверяем вызов SMS
Зависимость медленнаяБаза данных, внешний API
Зависимость нестабильнаяПогодный API, курс валют
Зависимость дорогаяПлатёжный шлюз (каждый тест — реальные деньги)

Пример из реальности

Без stub (плохо):

Тест создаёт заказ → API вызывает реальный SMS-шлюз → уходит реальное SMS → медленно, дорого, раздражает.

Со stub (хорошо):

Тест создаёт заказ → API вызывает заглушку SMS-шлюза → заглушка мгновенно отвечает “успешно” → быстро, дёшево, никому не звонит.

Что должен знать аналитик

ВопросПочему важен
“Какие зависимости заглушены в тестах?”Понимать, что реально тестируется, а что нет
“Есть ли тесты с реальными зависимостями?”Интеграционные тесты нужны для проверки настоящих вызовов

Mock (Двойник)

Что это

Mock — это объект, который запоминает, как его вызывали. После теста вы проверяете: “Был ли вызван метод?”, “С какими параметрами?”, “Сколько раз?”.

Когда использовать

СитуацияПример
Тест проверяет взаимодействие“При оплате заказа API вызывает платёжный шлюз”
Важно, что зависимость вызвана“При отмене заказа API отправляет событие в Kafka”
Важны параметры вызова“При создании пользователя API вызывает сервис уведомлений с правильным email”

Пример из реальности

Mock:

Тест создаёт пользователя → API вызывает мок-сервис уведомлений → мок запоминает, что его вызвали с email “ivan@example.com”.

После теста проверка: “Был ли вызван метод отправки уведомления?” → Да. “С правильным email?” → Да.

Что должен знать аналитик

ВопросПочему важен
“Что именно проверяют моки?”Понимать, какие взаимодействия тестируются
“Есть ли тесты, которые проверяют, что вызов НЕ произошёл?”Например, “при ошибке валидации не вызывать платёжный шлюз”

Stub vs Mock

ХарактеристикаStubMock
НазначениеВозвращать данныеПроверять взаимодействие
Что проверяетСостояние (результат)Поведение (вызовы)
ПримерВсегда возвращает “успех”Проверяет, что вызвали метод “оплатить”
Вопрос“Что вернулось?”“Что вызвалось?”

Аналогия:

  • Stub — кассир, который всегда говорит “билет действителен”. Неважно, кто и когда спросил.
  • Mock — кассир, который записывает, кто и когда спрашивал. Потом вы проверяете журнал.

Fake (Фейк)

Что это

Fake — это упрощённая, но рабочая реализация зависимости. Например, вместо реальной базы данных — in-memory коллекция.

Когда использовать

СитуацияПример
Нужна логика, не только ответыТест проверяет, что пользователь сохраняется в БД
Нужно несколько шаговСначала сохранить, потом прочитать, потом обновить

Пример

Вместо PostgreSQL → in-memory словарь (HashMap).

"Сохранить пользователя" → положить в map
"Найти пользователя по ID" → достать из map

Что даёт: Тест быстрый, не требует реальной БД, но проверяет логику сохранения и чтения.

Spy (Шпион)

Что это

Spy — это обёртка вокруг реального объекта. Он записывает вызовы, но делегирует их реальной реализации.

Когда использовать

СитуацияПример
Хотим частично переопределить поведениеРеальная БД, но переопределили один метод
Хотим проверить вызовы на реальном объектеПроверить, что логгер вызван

Инструменты для Mocking и Stubbing

ЯзыкИнструменты
JavaMockito, EasyMock, JMockit
Pythonunittest.mock, pytest-mock
Gotestify/mock, gomock
JavaScriptJest, Sinon.js, Mock Service Worker
C#Moq, NSubstitute, FakeItEasy

WireMock (для тестирования API)

WireMock — это инструмент для создания заглушек HTTP API.

ВозможностьПример
StubGET /payment/123 → 200 OK
MockПроверить, что API вызвал /payment с правильным телом
Симуляция ошибокGET /payment/123 → 500 Internal Error
Симуляция задержекGET /payment/123 → ответ через 5 секунд

Когда использовать реальные зависимости

ЗависимостьMock/StubРеальнаяПочему
База данныхМодульные тестыИнтеграционныеМодульные — быстро, интеграционные — проверяют миграции
Внешнее APIВсегда (Stub)РедкоДорого, нестабильно
Очереди (Kafka)Модульные тестыИнтеграционныеПроверить сериализацию, партиции
Файловая системаStubИнтеграционныеПрава доступа, блокировки
ВремяStub (фиксированное время)НикогдаТест должен быть детерминированным
СлучайностьStub (фиксированное значение)НикогдаТест должен быть детерминированным

Что аналитик должен знать

Какие вопросы задать команде

ВопросЗачем
“Какие зависимости заглушены в модульных тестах?”Понимать границы тестирования
“Есть ли интеграционные тесты с реальными зависимостями?”Убедиться, что заглушки соответствуют реальности
“Как часто обновляются заглушки при изменении API?”Предотвратить расхождение мока и реальности
“Тестируются ли ошибки зависимостей?”Таймауты, 500 ошибки, невалидные ответы

Риски избыточного мокинга

РискОбъяснение
Тесты проходят, а система падаетМок не соответствует реальному API
Тесты зелёные, но интеграция не работаетНе проверены форматы данных, протоколы
Сложно поддерживатьКаждое изменение API требует изменения моков

Риски недостаточного мокинга

РискОбъяснение
Тесты медленныеЖдут реальную БД, сеть
Тесты нестабильные (flaky)Внешнее API может упасть
Тесты дорогиеКаждый запуск — деньги (платные API)

Пример из реального проекта

Ситуация

API интернет-магазина при создании заказа:

  1. Проверяет остатки в БД
  2. Вызывает платёжный шлюз
  3. Отправляет событие в Kafka
  4. Отправляет email через SMTP

Стратегия тестирования

УровеньЧто заглушеноЧто реальное
Модульные тестыБД (in-memory), платёжный шлюз (stub), Kafka (mock), SMTP (mock)Логика создания заказа
ИнтеграционныеПлатёжный шлюз (WireMock), SMTP (Fake)БД, Kafka
E2EНичегоВсё реальное (тестовые аккаунты)

Что проверяют моки на модульном уровне

  • Платёжный шлюз: проверить, что вызван с правильной суммой
  • Kafka: проверить, что отправлено событие order.created
  • SMTP: проверить, что email отправлен на правильный адрес

Распространённые ошибки

Ошибка 1: Мок слишком умный

Мок проверяет не только факт вызова, но и внутреннюю реализацию.

Пример: Мок проверяет, что метод вызван три раза, а важно только то, что вызван хотя бы раз.

Решение: Проверять поведение, а не реализацию.

Ошибка 2: Мок возвращает только успех

Нет тестов на ошибки внешних зависимостей.

Решение: Добавить тесты: “платёжный шлюз вернул ошибку → API вернул 502”.

Ошибка 3: Заглушка не соответствует реальности

Заглушка возвращает {"status": "ok"}, а реальный API — {"success": true}.

Решение: Периодически запускать интеграционные тесты с реальными API.

Ошибка 4: Избыточное мокинге

Заглушены даже простые зависимости, которые можно использовать реальные.

Решение: Для быстрых и стабильных зависимостей (время, случайность) использовать реальные.

Ошибка 5: Нет тестов на отсутствие вызова

Проверяем, что при ошибке валидации не вызывается платёжный шлюз.

Решение: Добавить мок с проверкой, что метод НЕ был вызван.

Резюме

  1. Mocking и Stubbing — техники создания дублёров для зависимостей. Позволяют тестировать компонент в изоляции.

  2. Stub (заглушка) — возвращает предопределённые ответы. Не проверяет вызовы. Используется, когда важен только результат.

  3. Mock (двойник) — запоминает вызовы и проверяет их. Используется, когда важно, как и с какими параметрами вызвали зависимость.

  4. Fake (фейк) — упрощённая рабочая реализация. Например, in-memory база данных.

  5. WireMock — инструмент для заглушек HTTP API (для интеграционных тестов).

  6. Когда заглушать: внешние API, медленные/дорогие/нестабильные зависимости.

  7. Когда использовать реальные: БД в интеграционных тестах, простые зависимости.

Проверка знаний

Вопрос 1 из 4
Что делает stub?
На какой вопрос отвечает mock?
Зачем в тестах подменять реальный SMS-шлюз или внешний API дублёром?
Что является главным эффектом mocking/stubbing для тестовой стратегии?

Вопросы, где были ошибки