Введение в тестирование контрактов, часть 6: двусторонне направленные контракты |
28.07.2022 00:00 |
Автор: Баз Дейкстра (Bas Dijkstra) В предыдущих статьях серии о тестировании ориентированных на потребителя контрактов и Pact мы обсуждали CDCT, разобрались, как использовать Pact для поддержки CDCT, как автоматизировать процесс и сделать его частью процессов CI/CD, и посмотрели, что будет, когда на стороне потребителя меняются ожидания. В шестой и последней статье мы посмотрим, как добавить в процесс CDCT новый сервис, и как это можно упростить при помощи подхода, который называется двусторонне направленным тестированием контрактов. Предупреждение: двусторонне направленное тестирование контрактов существует только в Pactflow и недоступно в OSS Pact Broker. Новый игрок: поставщик сервиса оплаты Чтобы оформление заказов было бесперебойным и бесшовным, наш магазин бутербродов решил позволить заказчику оплачивать заказы онлайн через стороннего провайдера оплаты. Этот сервис оплаты используется сервисом заказов, который мы уже рассмотрели в прошлых статьях, и наша архитектура теперь выглядит как-то так: Чтобы убедиться, что интеграция сервиса заказов (потребителя) и сервиса оплаты (провайдера) работает как надо, команда сервиса заказов хочет добавить контрактные тесты для этой интеграции – так же, как и для интеграции с сервисом адресов. Однако команда, ответственная за разработку и поставку сервиса оплаты, не хочет пользоваться Pact, так как, по их мнению, он слишком сильно повлияет на их текущий подход к разработке и тестированию. По словам команды Pactflow, это довольно распространенная ситуация в командах, думающих о внедрении CDCT: работа с Pact способом, описанным в предыдущей статье, требует значительных усилий на стороне как потребителя, так и поставщика. В базу кода нужно добавлять новые зависимости, написать определения и тесты для Pact, добавить в билд дополнительные шаги… Это и удерживает множество команд от внедрения контрактного тестирования. Недавно команда Pactflow выпустила решение этой проблемы, упрощающее командам начало работы с тестированием контрактов. Встречайте – двусторонне направленное тестирование контрактов. Двусторонне направленное тестирование контрактов В "традиционном" CDCT потребитель генерирует контракт, используя Pact, а затем передает его провайдеру для верификации, используя Pact Broker. Провайдер забирает контракт, проводит верификацию и загружает результаты верификации обратно в Pact Broker. Используя can-i-deploy, потребитель и провайдер могут проверить, безопасно ли выпускать новую версию в прод. Двусторонне направленное тестирование контрактов, или BDCT, использует другой алгоритм: в BDCT и потребитель, и провайдер генерируют свои версии контрактов и загружают их в Pactflow, а он затем проверяет оба контракта на совместимость. Как только это сделано, и потребитель, и провайдер могут снова воспользоваться can-i-deploy перед выходом на прод, чтобы проверить наличие проблем интеграции. Для упрощения генерации контрактов BDCT не требует "полного" внедрения Pact. Вместо этого можно воспользоваться существующими тестами, инструментами и спецификациями, внедрив их в решение для контрактного тестирования. Документация Pact отлично описывает все тонкости, а также перечисляет все поддерживаемые инструменты и технологии, поэтому не буду тут повторяться. Вместо этого давайте разберемся на примере. Сторона потребителя – генерирование контракта из макетов WireMock Для проверки процесса оплаты заказа бутербродов команда заказов уже пользуется WireMock, имитируя провайдера сервиса оплаты с его помощью. Так как WireMock – инструмент, который уже поддерживается командой Pactflow в BDCT, имитируемое им поведение можно использовать для генерации контракта на стороне потребителя. В этом примере мы посмотрим на операции HTTP GET, которые получают детали оплаты для конкретного ID заказа. Для других операций процесс аналогичен (к примеру, для POST-операции передачи платежа за заказ). Вот как будет выглядеть тест успешного получения деталей платежа: private static final UUID ID = UUID.fromString("8383a7c3-f831-4f4d-a0a9-015165148af5"); Схожие тесты есть для ситуации, когда платеж для заказа не найден (возвращается HTTP 404 с пустым телом ответа), и ситуации, когда ID заказа невалиден (возвращается HTTP 400, также с пустым телом). Для генерации BDCT-контракта потребителя из этих тестов и макетов команда Pactflow разработала библиотеку wiremock-pact-generator. После добавления этой зависимости в проект нам остается только поменять код тестов, добавив WireMockPactGenerator как слушатель в экземпляр WireMock: @BeforeAll Можно видеть, что при добавлении WireMockPactGenerator нам нужно только идентифицировать потребителя (order_consumer) и провайдера (payment_provider), и все готово к работе. При запуске тестов, вызывающих экземпляр WireMock со слушателем Pact, генерируется контракт, который размещается в папке /target/pacts – так же, как и в "традиционном" CDCT. Контракт, однако, выглядит чуть иначе, особенно в части, задающей ожидания для тела ответа в случае успешного получения деталей платежа: As you can see, when adding the WireMockPactGenerator, the only thing we need to supply is an identification for the consumer (order_consumer) and the provider (payment_provider) and we’re good to go. "response": { Обратите внимание, контракт не задает явное соответствие элементов тела, возвращенного провайдером, примерам в контракте только по типу, как в "традиционных" контрактах. В BDCT, как обсуждалось выше, верификация осуществляется Pactflow, а не провайдером (так как он загружает свой собственный контракт), и верификация на основе схемы (то есть верификация типа или формы данных) – это единственный тип выполняемой верификации. Иными словами, Pactflow сравнивает форму ожидаемого ответа (выданного потребителем) с формой реального ответа (выданной провайдером) и сообщает о несоответствиях, если они найдены. У такого поведения есть ряд интересных преимуществ (отметим, что это прямая цитата из письма Мэтта и крайне отзывчивой команды Pact):
Как только контракт сгенерирован из тестов WireMock на стороне потребителя, их можно загружать, как и традиционные контракты: mvn pact:publish Смотря на Pactflow, можно увидеть, что добавилась новая интеграция между order_consumer и payment_provider, и она еще не верифицирована: Вот как это выглядит на стороне потребителя. Перейдем на сторону провайдера. Сторона провайдера – использование существующей спецификации API в качестве контракта Используя BDCT для быстрого внедрения контрактных тестов, провайдеры могут повторно использовать существующие сервисные спецификации в качестве контракта для верификации в Pactflow. На данный момент поддерживаются только спецификации OpenAPI (OAS), но это довольно распространенный стандарт спецификаций, и множество провайдеров уже могут выиграть от этой возможности. Следует отметить, что у провайдера, скорее всего, уже есть тесты, проверяющие, что конечные точки провайдера соответствуют ожиданиям спецификации API (OAS). Результаты этих тестов можно загружать вместе с OAS в качестве дополнительного доказательства. Как правило, CI-процесс провайдера выполняет эти функциональные тесты до публикации контракта/OAS, что означает, что упавшие тесты воспрепятствуют загрузке контракта в Pactflow. На момент создания этой статьи загрузка OAS в Pactflow проводится через довольно заковыристый вызов API, однако команда Pactflow работает над включением этого в инструментарий Pactflow CLI, что, вне всяких сомнений, упростит процесс. Вот вызов PUT, который сейчас требуется для загрузки контракта провайдера (как можно видеть, я воспользовался Postman, однако cUrl или любой другой клиент API тоже сработают): PUT /contracts/provider/<<PROVIDER_NAME>>/version/<<PROVIDER_VERSION>> HTTP/1.1 Если все прошло успешно, мы получим код ответа HTTP 201, сообщающий, что контракт провайдера успешно загружен в Pactflow. Обновив экран, мы увидим, что Pactflow не только получил контракт, но и сравнил контракты потребителя и провайдера: В этом случае контракт потребителя (сгенерированный из макета WireMock спецификаций API) и контракт провайдера (OAS) совместимы, и это значит, что order_consumer и payment_provider могут воспользоваться can-i-deploy для проверки текущего статуса верификации контракта перед выходом на прод. Как можно видеть в примере, BDCT – очень мощный способ начать работу с тестированием контрактов. Так как BDCT пользуется существующими инструментами и спецификациями, для загрузки и верификации ваших первых контрактов нужно немного по сравнению с традиционным "полноценным" CDCT. По ходу того, как поддержку Pact получит большее количество инструментов и стандартов спецификаций, популярность BDCT, скорее всего, возрастет. Итак, какой же подход выбрать? BDCT или CDCT? Как обычно, единственный верный ответ тут – "зависит". Отвечу вам этим сравнением, созданным командой Pact. Серия статей про CDCT и Pact на этом завершена. Я уверен, что буду продолжать интересоваться CDCT, BDCT и Pact в будущем, поэтому, возможно, еще появятся статьи о специфических тонкостях в этой области, однако эти шесть статей должны стать хорошим введением в концепцию тестирования контракта, инициированного потребителем, двусторонне направленного тестирования контрактов и инструментария, поддерживающего эти подходы. Весь код статьи находится в GitHub. |