Назад к вопросам
Middle+
79
questionbank

Расскажи о контрактном тестировании микросервиса.

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Контрактное тестирование фокусируется на проверке взаимодействия между микросервисами на уровне их API (контракта). Цель — убедиться, что потребитель (сервис, использующий API) и поставщик (сервис, предоставляющий API) соблюдают определенный "контракт" (схему запросов/ответов, формат данных, ожидания).

Основные принципы:

  • Потребитель-движет: Тесты основаны на ожиданиях потребителя. Потребитель определяет, какие данные и форматы ему нужны.
  • Двустороннее тестирование: Проверяется как сторона потребителя (умеет ли он работать с текущим контрактом), так и сторона поставщика (соответствует ли его реализация ожиданиям потребителя).
  • Быстрота: Тесты выполняются быстро, часто в рамках конвейера CI/CD, так как не требуют развертывания всех зависимых сервисов.

Процесс обычно включает:

  1. Потребитель создает тесты, описывающие свои ожидания от API поставщика. Эти тесты генерируют "контракт".
  2. Контракт публикуется (например, в централизованном репозитории).
  3. Поставщик использует опубликованный контракт для верификации своей реализации API. Он запускает тесты (обычно на стороне поставщика), которые проверяют, соответствует ли его API контракту.
  4. В случае расхождения между ожиданиями потребителя и реализацией поставщика, тест падает, указывая на нарушение контракта.

Инструменты:

  • Pact (наиболее популярный)
  • Spring Cloud Contract
  • Swagger/OpenAPI с инструментами валидации

Преимущества:

  • Раннее выявление ошибок интеграции: Проблемы с совместимостью обнаруживаются еще до развертывания.
  • Уменьшение необходимости интеграционных тестов: Снижает зависимость от сложных и медленных сквозных тестов.
  • Независимое тестирование: Позволяет тестировать сервисы независимо друг от друга.
  • Четкое определение ожиданий: Явно определяет контракт между сервисами.

Недостатки:

  • Не заменяет полностью интеграционные тесты (только проверяет взаимодействие на уровне API).
  • Требует дисциплины в версионировании контрактов.
  • Может быть сложнее для сервисов с очень сложными контрактами.

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

  1. Потребитель (например, сервис 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));
        }
    }
    
  2. При выполнении consumer-side тестов генерируется файл контракта (.json файл).
  3. Этот файл контракта публикуется в сервис Pact Broker или другой централизованный репозиторий.
  4. Поставщик (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));
        }
    }
    
  5. verifier на стороне поставщика вызывает реальный API поставщика согласно спецификации контракта и сравнивает полученный ответ с ожидаемым в контракте.

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