API и интеграции

3 мин чтения ·

Contract Testing: два зелёных сервиса ещё не означают работающий заказ

Команда заказов изменила ответ, команда оплаты не обновилась. Оба сервиса прошли собственные тесты — покупатель всё равно не смог завершить покупку.

Сервис заказов выпустил новую версию. Его тесты прошли. У сервиса оплаты тоже всё зелёное. Только покупатель теперь не может завершить оформление: потребитель ожидал статус в одном виде, а поставщик начал возвращать его иначе.

Оба приложения технически исправны по отдельности. Но их общий договор оказался нарушен. Contract Testing нужен, чтобы обнаруживать такие несовместимости до релиза — без необходимости каждый раз поднимать всю систему и собирать на проверку все команды.

Что именно проверяет контракт

В API есть не только документация и схема полей. Есть конкретные ожидания потребителя: он делает запрос определённого вида, рассчитывает получить нужный статус и поля, понимает ошибки в согласованном формате. Если ответ формально допустим, но клиент не умеет с ним работать, интеграция сломалась.

В consumer-driven contract testing потребитель описывает минимальные взаимодействия, на которые действительно полагается. Поставщик затем проверяет эти ожидания на своей реализации. Не нужно копировать весь ответ целиком, если потребителю важны только два поля. Иначе любое безвредное дополнение к API сделает тест красным.

Важно, что контрактный тест не равен обычному mock-тесту клиента. Mock, который самостоятельно придумала клиентская команда, может подтвердить любое удобное предположение. Ценность появляется, когда ожидания потребителя проверяются на стороне настоящего поставщика.

Проверка должна происходить до выпуска

Представим, что команда заказов хочет изменить формат поля. Ещё до развёртывания она запускает проверку контрактов актуальных потребителей. Если одно приложение больше не сможет прочитать ответ, конфликт становится видимым в CI, а не в обращениях клиентов.

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

В документации Pact проверка на работающей реализации поставщика в CI рассматривается как часть такого процесса. Инструмент не отменяет переговоров о правилах API — он делает последствия изменений раньше заметными.

Что этот тест не доказывает

Успешный контрактный тест подтверждает определённые взаимодействия. Он не доказывает, что пользователь оплатил заказ, получил письмо и увидел корректную сумму. У двух сервисов могут быть совместимые поля и одновременно неправильные бизнес-правила.

Также отдельно нужно проверять авторизацию, реальные данные, устойчивость к таймаутам, нагрузку и критичные сквозные сценарии. Контрактные тесты не заменяют их, а позволяют сократить количество дорогих проверок, которые иначе приходилось бы запускать после каждого небольшого изменения.

Особенно опасны тесты, которые навечно фиксируют случайные детали реализации. Если потребитель ничего не делает с лишним полем или не зависит от порядка атрибутов в ответе, зачем блокировать его добавление?

Кто удалит ожидания давно ушедшего клиента

Когда один API обслуживает несколько продуктов, контракты со временем устаревают. Потребитель мог перестать существовать, но его проверка продолжает запрещать нормальные изменения. Нужны владельцы контрактов и понятный жизненный цикл их удаления.

Я бы начал не с закупки платформы тестирования, а с последней реальной интеграционной аварии. Какая несовместимость её вызвала? Мог бы тест потребителя обнаружить её раньше? Сколько независимых команд регулярно меняют эти контракты?

Если интерфейс меняется синхронно одной маленькой командой, отдельная сложная инфраструктура может оказаться лишней. Но когда релизы независимы и цена поломки высокая, проверяемый контракт снижает стоимость согласований.

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