Введение в тестирование контрактов, часть 2: общая информация о тестировании контрактов |
22.03.2022 00:00 |
Автор: Баз Дейкстра (Bas Dijkstra) В предыдущей статье мы познакомились с фиктивным онлайн-магазином, торгующим бутербродами, и его неплотно связанными компонентами. Мы также увидели, что так как компоненты разрабатывались разными командами, интеграция и end-to-end тестирование осложнены рядом проблем. В этой статьей вы узнаете, что такое тестирование контрактов, как выглядит тестирование контрактов, ориентированных на потребителя, и как оно справляется с проблемами нашего онлайн-магазина бутербродов. Что осложняет интеграционное тестирование? Предыдущая статья содержала ряд проблем, усложняющих интеграционное тестирование в нашем случае:
Корневая причина большинства вышеописанных проблем – это восприятие интеграционного тестирования как синхронной деятельности. Для выполнения интеграционного тестирования разные компоненты, необходимые для определенного теста, должны быть доступны в одно и то же время – как правило, перед тем, как стартует тест. Чем шире масштаб интеграционного теста, тем больше компонентов в нем задействовано, и тем сложнее будет настройка подходящего тест-окружения. Наша ситуация намеренно ограничена, но в реальном мире у нас на руках могут быть десятки или даже иногда сотни компонентов, формирующих тестируемое приложение, и все из них должны быть доступными каждый раз, когда запускается тест. Неудивительно, что многим командам и компаниям трудно добиться достаточного интеграционного и end-to-end тестового покрытия! Введение в тестирование контрактов В игру вступает тестирование контрактов. Цель тестирования контрактов – трансформировать интеграционное тестирование, сделав его из синхронного асинхронным. Вместо того, чтобы поднимать крупные участки, а иногда даже все приложение целиком, чтобы запустить тесты, тестирование контрактов фокусируется на индивидуальных парах компонентов – потребителе и провайдере. В любой распределенной системе компоненты совместно работают, обмениваясь данными. Потребитель запрашивает данные у провайдера (или отправляет данные провайдеру, или обновляет или удаляет существующие данные), а провайдер отвечает или запрошенными данными (или подтверждающим сообщением), или подходящим ответом, информирующим, что что-то пошло не так. Контрактное тестирование смотрит на каждую индивидуально связанную пару компонентов и проверяет, все ли пары способны общаться в соответствии со спецификацией (или ожиданиями) в любой момент времени. Если все пары способны взаимодействовать друг с другом, на уровне интеграции не должно быть никаких проблем. Заметьте, что контрактное тестирование не заменяет функциональное тестирование отдельных компонентов. Оно нацелено на интеграционные проблемы, возникающие, когда разные компоненты обмениваются данными, но оно не проверяет внедрение каждого из этих компонентов. Эта задача относится к функциональному тестированию, и за нее отвечают команды, разрабатывающие каждый конкретный компонент. Чтобы детальнее узнать о концепции тестирования контрактов, прочитайте эту статью. Как работает тестирование контрактов? Тестирование контрактов, как следует из его имени, использует контракты для формализации ожиданий всех пар "потребитель – провайдер" по отношению к поведению "второй половины". В целом оно делится на два типа, и основное различие между ними в том, кто отвечает за создание контракта:
У обоих подходов есть плюсы и минусы, некоторые из которых описаны здесь. В следующей части статьи и последующих статьях мы будем рассматривать только тестирование контрактов, ориентированных на потребителя (CDCT). Это также самый часто используемый на практике вид контрактного тестирования, и инструменты, которые мы будем рассматривать, тоже написаны для поддержки именно CDCT/ Полный цикл CDCT, покрывающий верификацию, что пара потребитель-провайдер может взаимодействовать, состоит из четырех шагов:
Этот процесс и делает CDCT (и тестирование контрактов в целом) асинхронным методом интеграционного тестирования: потребитель и провайдер выполняют шаги, за которые они отвечают, в ходе своего процесса разработки, не нуждаясь в доступности связанных с ними компонентов. Все ожидания потребителей от провайдеров письменно зафиксированы в контракте, и каждая сторона проводит интеграционное тестирование в ходе своего собственного процесса разработки и релиза. В качестве примера пройдем по этим шагам более детально, используя пример из нашего магазина бутербродов. В этом примере мы сконцентрируемся на Customer API в качестве потребителя, и Address API в качестве провайдера. Пожалуйста, посмотрите предыдущую статью для детального объяснения, что делают эти компоненты. Если конкретнее, мы возьмем GET-операцию, доступную через Address API и инициируемую Customer API для получения подробной информации о конкретном адресе, ассоциированном с покупателем: GET /address/{id} Пока что предположим, что нас интересуют только HTTP-коды ответов, возвращаемых операцией GET. В следующей статье мы подробно разберем другие ожидания Customer API от ответа Address API – в частности, данные, возвращаемые им. В плане HTTP-кодов статуса у Customer API могут быть следующие ожидания:
На первом шаге процесса CDCT Customer API создает контракт, содержащий эти ожидания в стандартизированном формате. Customer API затем публикует этот контракт в централизованном месте, где провайдер, в данном случае Address API, может забрать его для верификации. На третьем шаге провайдер проверяет, соответствует ли его текущая реализация всем формализованным в контракте ожиданиям, и, наконец, сообщает результаты верификации в централизованном месте, чтобы потребитель узнал, что: a) Провайдер соответствует всем ожиданиям и все хорошо, или b) Провайдер не может соответствовать всем ожиданиям, и нужно начать переговоры, чтобы разрешить проблему интеграции. Как можно видеть на этом примере, контракт – это соглашение между единственным потребителем (Customer API в данном случае) и единственным провайдером (Address API в нашем примере). Это означает, что если компонентом провайдера пользуется Х разных потребителей, и все из них внедрили CDCT, провайдеру нужно соответствовать ожиданиям Х(*) разных контрактов, чтобы убедиться в отсутствии интеграционных проблем. Очевидно, что чем больше потребителей, тем выше риск потенциальных конфликтов интересов. Мы подробно разберем пример такого конфликта в дальнейших статьях. (*) В примере с нашим магазином бутербродов Х равен 2, так как Address API используется и Customer API, и Order API. В этой статье вы узнали о целях тестирования контрактов и о том, что оно направлено на превращение интеграционного тестирования из синхронной деятельности в асинхронную. Если вы еще не уверены, подходит ли вам тестирование контрактов, то эта статья дает хороший обзор плюсов и минусов тестирования контрактов в разных контекстах. В следующей статье мы (наконец-то!) погрузимся в код и увидим, как внедрять шаги CDCT, описанные здесь, используя Pact. |