JavaRush /Курсы /Модуль 5. Spring /Лекция 266: Введение в контрактное тестирование (Pact)

Лекция 266: Введение в контрактное тестирование (Pact)

Модуль 5. Spring
27 уровень , 5 лекция
Открыта

Давайте сразу перейдем к сути. Когда микросервисы взаимодействуют друг с другом, они становятся как два разработчика, которые договорились: "Я тебе отправлю файл в формате JSON с такими полями!". В контрактном тестировании этот "договор" называется контрактом. Идея состоит в том, что взаимодействия между сервисами проверяются на соответствие заранее заданным условиям, чтобы убедиться, что никто случайно не нарушил "договор".

Пример: Представьте, что вы работаете над микросервисом "Заказчик", который делает HTTP-запросы к микросервису "Продукты". "Продукты" предоставляет в ответ JSON с информацией о товаре. Вы можете написать контрактное тестирование, чтобы гарантировать, что "Продукты" всегда возвращает корректный JSON, соответствующий ожиданиям "Заказчика".

Почему это важно?

В микросервисной архитектуре, где каждый сервис независим, но взаимодействует с другими, обновление одной части системы может легко сломать другую. Например, если команда разработчиков сервиса "Продукты" внезапно изменит структуру JSON-ответа, то сервис "Заказчик" начнет падать с ошибками. Контрактное тестирование помогает избежать таких ситуаций:

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

Различие между интеграционным и контрактным тестированием

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


Основы работы с Pact

Pact — это популярный инструмент для контрактного тестирования. Он служит посредником между двумя сторонами:

  • Потребителем (consumer) — сервисом, который отправляет запросы.
  • Провайдером (provider) — сервисом, который отвечает на запросы.

Pact позволяет создавать и проверять контракты между этими сторонами.

Основной принцип работы Pact

  1. Создание контракта: потребитель создает контракт, описывающий, какие запросы он отправляет и какие ответы ожидает от провайдера.
  2. Проверка контракта: провайдер проверяет контракт, чтобы гарантировать, что он может правильно отвечать на запросы потребителя.

Пример потока работы:

  1. Сервис "Заказчик" генерирует контракт, где указано:
    • Я отправлю GET-запрос на /products/1
    • Я ожидаю в ответе JSON с полями id, name, price
  2. Этот контракт передается сервису "Продукты".
  3. Сервис "Продукты" использует Pact для проверки, что он может отправить такой JSON.

Пример использования Pact для микросервисов

Перейдем к практике. Представим, что у нас есть два микросервиса:

  • Consumer (потребитель): сервис "Заказчик", который запрашивает информацию о товарах.
  • Provider (провайдер): сервис "Продукты", который возвращает информацию о товарах.

Шаг 1: Создание контракта на стороне потребителя

Мы начнем с "Заказчика". В этом сервисе мы описываем, какой запрос и ответ ожидаем от "Продуктов".

Перед началом работы добавим библиотеку Pact в наш build.gradle:

dependencies {
    testImplementation 'au.com.dius.pact.consumer:junit5:4.5.7'
}

Написание теста с использованием Pact


@PactTestFor(providerName = "ProductService")
public class ProductConsumerContractTest {

    @Pact(consumer = "CustomerService")
    public RequestResponsePact createPact(PactDslWithProvider builder) {
        return builder
                .given("Product with ID 1 exists")
                .uponReceiving("A request for product with ID 1")
                .path("/products/1")
                .method("GET")
                .willRespondWith()
                .status(200)
                .body("{\"id\": 1, \"name\": \"Laptop\", \"price\": 1200.00}")
                .toPact();
    }

    @Test
    @PactTestFor(pactMethod = "createPact")
    public void testConsumerBehaviour(MockServer mockServer) {
        // Используем mockServer для эмуляции API "Продуктов"
        String response = new RestTemplate().getForObject(mockServer.getUrl() + "/products/1", String.class);

        // Проверяем, что ответ соответствует ожиданиям
        assertEquals("{\"id\": 1, \"name\": \"Laptop\", \"price\": 1200.00}", response);
    }
}

Объяснение:

  • Мы создаем контракт с помощью Pact.
  • В контракте указано, что потребитель (CustomerService) делает GET-запрос на /products/1 и ожидает определенный JSON-ответ.
  • Pact поднимает MockServer для тестирования поведения потребителя.

Шаг 2: Проверка контракта на стороне провайдера

Теперь передадим контракт в сервис "Продукты" и проверим, что он соответствует ожиданиям.

Добавим Pact в провайдерский сервис:

dependencies {
    testImplementation 'au.com.dius.pact.provider:junit5:4.5.7'
}

Проверка контракта


@Provider("ProductService")
@PactBroker(host = "localhost", port = "9292")  // Брокер Pact для хранения контрактов
public class ProductProviderContractTest {

    @TestTemplate
    @ExtendWith(PactVerificationInvocationContextProvider.class)
    public void validatePacts(PactVerificationContext context) {
        context.verifyInteraction();
    }

    @State("Product with ID 1 exists")
    public void productExists() {
        // Настраиваем тестовую базу данных или моки для состояния "Продукт с ID 1 существует"
    }
}

Объяснение:

  • Мы используем Pact для проверки контракта.
  • В методе productExists задаем начальное состояние (например, добавляем запись в базу).
  • Pact автоматически проверяет, что сервис "Продукты" соответствует контракту.

Преимущества использования Pact

  1. Раннее нахождение проблем: проблемы интеграции между потребителем и провайдером сразу становятся заметны.
  2. Изоляция тестов: потребитель и провайдер тестируются независимо.
  3. Документация взаимодействий: контракты служат авто-документацией API.

Заключение темы

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

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ