Назад к вопросам
Middle+
79
questionbank
Расскажи о контрактном тестировании микросервиса.
Sobes Copilot
Получайте ответы в реальном времени
Ответ от нейросети
sobes.tech AI
Контрактное тестирование фокусируется на проверке взаимодействия между микросервисами на уровне их API (контракта). Цель — убедиться, что потребитель (сервис, использующий API) и поставщик (сервис, предоставляющий API) соблюдают определенный "контракт" (схему запросов/ответов, формат данных, ожидания).
Основные принципы:
- Потребитель-движет: Тесты основаны на ожиданиях потребителя. Потребитель определяет, какие данные и форматы ему нужны.
- Двустороннее тестирование: Проверяется как сторона потребителя (умеет ли он работать с текущим контрактом), так и сторона поставщика (соответствует ли его реализация ожиданиям потребителя).
- Быстрота: Тесты выполняются быстро, часто в рамках конвейера CI/CD, так как не требуют развертывания всех зависимых сервисов.
Процесс обычно включает:
- Потребитель создает тесты, описывающие свои ожидания от API поставщика. Эти тесты генерируют "контракт".
- Контракт публикуется (например, в централизованном репозитории).
- Поставщик использует опубликованный контракт для верификации своей реализации API. Он запускает тесты (обычно на стороне поставщика), которые проверяют, соответствует ли его API контракту.
- В случае расхождения между ожиданиями потребителя и реализацией поставщика, тест падает, указывая на нарушение контракта.
Инструменты:
- Pact (наиболее популярный)
- Spring Cloud Contract
- Swagger/OpenAPI с инструментами валидации
Преимущества:
- Раннее выявление ошибок интеграции: Проблемы с совместимостью обнаруживаются еще до развертывания.
- Уменьшение необходимости интеграционных тестов: Снижает зависимость от сложных и медленных сквозных тестов.
- Независимое тестирование: Позволяет тестировать сервисы независимо друг от друга.
- Четкое определение ожиданий: Явно определяет контракт между сервисами.
Недостатки:
- Не заменяет полностью интеграционные тесты (только проверяет взаимодействие на уровне API).
- Требует дисциплины в версионировании контрактов.
- Может быть сложнее для сервисов с очень сложными контрактами.
Пример использования с Pact:
- Потребитель (например, сервис
Order Service) использует библиотеку Pact для создания consumer-side тестов.// Пример теста потребителя с Pact JVM @ExtendWith(PactConsumerTestExt.class) @PactTestFor(providerName = "ProductService", port = "8080") public class ProductServiceContractTest { @Pact(consumer = "OrderService") public RequestResponsePact createPact(PactDslWithRequest r) { return r.given("a product with id 1 exists") .uponReceiving("a request for product by id") .path("/products/1") .method("GET") .willRespondWith() .status(200) .headers(Map.of("Content-Type", "application/json")) .body(new PactDslJsonBody() .stringValue("id", "1") .stringValue("name", "Laptop") .numberType("price", 1200.00)) .toPact(); } @Test void testGetProductById(MockServer mockServer) throws IOException { // Логика вызова API поставщика через mockServer // Проверяем, что потребитель корректно обрабатывает мок-ответ HttpResponse response = Request.Get(mockServer.getUrl() + "/products/1").execute().returnResponse(); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } } - При выполнении consumer-side тестов генерируется файл контракта (
.jsonфайл). - Этот файл контракта публикуется в сервис Pact Broker или другой централизованный репозиторий.
- Поставщик (
Product Service) использует библиотеку Pact (например, Pact Provider Verifier) для верификации своей реализации API на основе контракта, полученного из Pact Broker.// Пример теста поставщика с Pact Provider Verifier @Provider("ProductService") @PactFolder("relative/path/to/pacts") // или использовать Pact Broker public class ProductServiceVerificationTest { @TestTemplate @ExtendWith(PactVerificationInvocationContextProvider.class) public void verifyPact(PactVerificationContext context) { context.verifyInteraction(); } @State("a product with id 1 exists") public void productWithId1Exists() { // Логика подготовки состояния данных в сервисе поставщика для теста } // Добавляем провайдер урла, где запущен тестируемый сервис @BeforeEach void beforeEach(PactVerificationContext context) { context.setTarget(new HttpTestTarget("localhost", 8080)); } } - verifier на стороне поставщика вызывает реальный API поставщика согласно спецификации контракта и сравнивает полученный ответ с ожидаемым в контракте.
Контрактное тестирование является важной практикой для обеспечения надежности и поддерживаемости систем на базе микросервисов.