Мок-серверы для QA: WireMock и компания — когда подменять зависимость заглушкой
В прошлый раз мы говорили про Testcontainers — как поднять реальные зависимости в тесте. Сегодня обратная сторона: когда зависимость реально поднять нельзя или не нужно — её подменяют мок-сервером. Это HTTP-заглушка, которая отвечает так, как вы запрограммировали: нужный статус, тело, задержку, ошибку.
Главное — понимать, когда мок уместен, что он должен уметь и где он врёт.
Зачем мокать
- Чужой / платный API: платёжки, карты, SMS, погода — дёргать реальный дорого, лимитировано или просто нельзя в CI.
- Нестабильная зависимость: сторонний сервис падает/тормозит — тесты не должны от этого флакать.
- Сервиса ещё нет: фронт/QA работают параллельно с бэком по согласованному контракту.
- Трудные ответы: 429, 503, таймаут, битое тело, частичный ответ — в реале не воспроизвести по требованию, а в моке — одной настройкой.
Что должен уметь хороший мок-сервер
- Матчинг запроса: стаб срабатывает по методу, пути, заголовкам, query, телу (точное/по шаблону/regex).
- Fault injection: задержки, коды ошибок, обрыв соединения, мусорное тело — для проверки устойчивости клиента.
- Stateful-сценарии: ответ меняется по шагам (создал → теперь существует), эмуляция состояний.
- Record & replay: записать реальный трафик и проигрывать как мок (стартовый набор стабов из прода).
- Verify: проверить, что клиент действительно сделал ожидаемый вызов (с нужным телом/заголовками).
- Templating: динамический ответ на основе запроса (echo id, текущее время).
Инструменты и их ниши
- WireMock — мощный (JVM, есть standalone/Docker): богатый матчинг, fault injection, proxy+record, verify. Рабочая лошадка для интеграционных тестов.
- MockServer — гибкие expectations, проксирование, на JVM; близкий конкурент WireMock.
- Mockoon — десктоп-GUI, мгновенный старт без кода; удобно для ручного QA и быстрых стабов.
- Prism — мок прямо из OpenAPI-спеки: заглушка автоматически соответствует контракту (и валидирует запросы по нему).
- Hoverfly — лёгкий proxy с record/replay (capture/simulate), хорош для снятия реального трафика.
Мок или реальная зависимость (Testcontainers)?
Простое правило:
- Реально (Testcontainers) — то, что ваше и поднимается локально: БД, брокер, кэш, ваш же сервис. Мок тут только спрячет реальные баги интеграции.
- Мок — внешнее и неподконтрольное: чужие/платные API, сервисы за границей сети, то, чего ещё нет, и редкие сбойные ответы.
Не мокайте то, что можно поднять по-настоящему. Мок своей же БД — это тест заглушки, а не системы.
Главный риск мока — контрактный дрейф
Заглушка застывает: реальный API поменял формат/коды, а ваш мок всё ещё отвечает по-старому. Тесты зелёные, прод падает. Как страховаться:
- Contract testing (Pact): провайдер и потребитель проверяют согласованность контракта автоматически.
- Мок из спеки: Prism генерит заглушку из OpenAPI — она не разойдётся со схемой, пока схема актуальна.
- Периодический smoke против реального API (в отдельном прогоне, не в каждом PR).
- Record-from-real: пересоздавать стабы из свежего реального трафика.
Мок как инструмент тестирования устойчивости
Самая недооценённая польза: моком тривиально воспроизвести то, что в реале не вызвать по кнопке.
- Таймаут и медленный ответ → проверяем клиентские таймауты и ретраи.
- 429/503 → проверяем backoff, circuit breaker, грациозную деградацию.
- Битое/частичное тело → парсинг не падает, ошибка обрабатывается.
- Обрыв соединения посреди ответа → клиент не зависает.
Частые ошибки
- Мок слишком розовый: всегда 200, никаких ошибок — клиент не протестирован на сбои.
- Мокают то, что можно поднять реально (своя БД/сервис).
- Нет
verify: тест зелёный, даже если клиент вообще не сделал вызов. - Стабы устарели и разошлись с реальным API (контрактный дрейф).
- Только happy-path, без 4xx/5xx/таймаутов.
- Хардкод портов/хостов вместо конфигурируемого base URL.
Чек-лист по мокам (10 пунктов)
- Мокается только внешнее/неподконтрольное; своё — поднимается реально.
- Матчинг запроса достаточно строгий (метод/путь/тело/заголовки).
- Есть негативные стабы: 4xx, 5xx, таймаут, битое тело.
- verify проверяет, что клиент сделал ожидаемый вызов.
- Контракт под защитой: Pact / мок из OpenAPI / запись из реального.
- Есть периодический smoke против реального API.
- Stateful-сценарии там, где ответ зависит от предыдущих шагов.
- Base URL зависимости конфигурируем (легко переключить мок/реал).
- Стабы версионируются вместе с тестами.
- Используется fault injection для проверки устойчивости клиента.
Итог
Мок-сервер — это про контроль: вы задаёте ровно те ответы, которые нужны, включая сбойные. Но именно поэтому мок легко делает тесты слишком зелёными и расходится с реальностью. Мокайте внешнее, держите контракт под защитой (Pact/OpenAPI/реальный smoke), обязательно тестируйте ошибки и проверяйте вызовы через verify. Реальную инфру — через Testcontainers, а не заглушкой.