ТестированиеТестирование API и интеграцийИнженер по интеграционному тестированию

При добавлении нового значения перечисления старый клиент аварийно завершается. Какое правило совместимости...

При добавлении нового значения перечисления старый клиент аварийно завершается. Какое правило совместимости должен проверять интеграционный тест?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Интеграционный тест должен проверять расширяемость перечислений: клиент не должен аварийно завершаться при получении неизвестного значения, если контракт допускает независимое обновление поставщика и потребителя. Неизвестное значение должно обрабатываться предсказуемо — например, через безопасный вариант unknown, игнорирование поля или контролируемую ошибку без падения процесса.

Исторический контекст

В распределённых системах поставщик API и его потребители часто выкатываются независимо. Жёсткое перечисление удобно для валидации, но добавление нового значения становится несовместимым изменением для старых клиентов, если они считают любое неизвестное значение ошибкой десериализации.

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

Постановка проблемы

Предположим, старый клиент знает значения new, paid и cancelled, а поставщик добавил refunded. Если клиент преобразует значение непосредственно во внутренний тип с жёстким набором вариантов, обработка ответа может завершиться исключением или остановкой бизнес-операции.

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

Подробное решение

Интеграционная проверка должна передать потребителю ответ с новым, заранее неизвестным ему значением перечисления и подтвердить согласованный результат: процесс не падает, сообщение не теряется бесконтрольно, а состояние операции фиксируется согласно политике обработки.

Важно различать два случая. Неизвестное, но синтаксически корректное значение проверяет расширяемость контракта; неверный тип, повреждённая структура или недопустимый формат проверяют уже валидацию входных данных. Нельзя смешивать эти проверки, иначе тест не покажет, какое именно свойство нарушено.

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

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

Ситуация из практики

Платёжный сервис возвращал статус операции в HTTP-ответе. После появления статуса partially_refunded старый кабинет начал выдавать ошибки при открытии списка платежей.

Рассматривались три варианта. Запретить поставщику добавлять значения без одновременного обновления всех клиентов было безопасно, но плохо подходило для независимых релизов. Перевести поле в свободную строку повышало совместимость, однако переносило контроль допустимых значений в прикладной код. Выбранным решением стала явная политика неизвестного значения: клиент отображал нейтральный статус, сохранял исходную строку для диагностики и не прерывал загрузку списка.

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

Что кандидаты часто упускают

  1. Достаточно ли проверить, что клиент не падает на неизвестном значении?

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

  1. Нужно ли требовать от каждого перечисления поддержки неизвестных значений?

Нет. Это зависит от семантики поля и жизненного цикла системы. Для отображаемого статуса безопасный fallback обычно реалистичен, а для критичного режима шифрования или способа расчёта неизвестное значение может требовать остановки операции. В контракте должна быть явно указана политика, а тест — проверять именно её.

  1. Почему тест поставщика с новым значением не заменяет интеграционный тест потребителя?

Поставщик может подтвердить, что он корректно сериализует новое значение, но не знает, как старый потребитель его разбирает. Интеграционная проверка на стороне потребителя выявляет ошибку десериализации, неверный fallback и побочные эффекты обработки. Для полной защиты нужны совместные контрактные проверки или тестовый контур, где ответ поставщика проходит через реальную логику потребителя.