Использовал ли ты контрактное тестирование (Contract Testing) для интеграционного тестирования?

«Использовал ли ты контрактное тестирование (Contract Testing) для интеграционного тестирования?» — вопрос из категории API тестирование, который задают на 24% собеседований AQA / Automation. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, применял контрактное тестирование, в частности с помощью фреймворка Pact, для решения проблем интеграции в микросервисной архитектуре. Мы столкнулись с ситуацией, когда сервис-потребитель (consumer) и сервис-провайдер (provider) разрабатывались разными командами, и частые breaking changes в API провайдера ломали потребителя.

Как это работало на практике:

  1. Со стороны потребителя (Consumer Tests): В наших автотестах для сервиса-потребителя мы использовали Pact для мокирования провайдера на основе ожидаемого контракта.

    // Пример на JavaScript (Jest + Pact)
    const { Pact } = require('@pact-foundation/pact');
    
    describe('User Service', () => {
      const provider = new Pact({
        consumer: 'FrontendService',
        provider: 'UserApiService',
      });
    
      beforeAll(() => provider.setup());
      afterEach(() => provider.verify());
      afterAll(() => provider.finalize());
    
      it('should receive user data', async () => {
        // Определяем ожидаемое взаимодействие (контракт)
        await provider.addInteraction({
          state: 'a user with id 123 exists',
          uponReceiving: 'a request for user data',
          withRequest: {
            method: 'GET',
            path: '/users/123',
          },
          willRespondWith: {
            status: 200,
            headers: { 'Content-Type': 'application/json' },
            body: {
              id: 123,
              name: 'John Doe', // Ожидаемая структура ответа
            },
          },
        });
    
        // Выполняем тест против мока Pact
        const user = await fetchUser(123);
        expect(user.name).toBe('John Doe');
      });
    });

    После успешного прогона тестов Pact публиковал контракт (JSON-файл) в общий брокер (например, PactFlow).

  2. Со стороны провайдера (Provider Verification): В пайплайне CI сервиса-провайдера запускалась задача верификации. Она забирала опубликованные контракты и прогоняла реальный API провайдера, проверяя, что он удовлетворяет всем ожиданиям потребителей.

    # Команда для верификации провайдера
    pact-verifier --provider-base-url http://localhost:8080 
                  --pact-broker-url https://our-broker.example.com 
                  --provider "UserApiService" 
                  --consumer-version-tags "prod"

Результат: Это позволило обнаруживать несовместимости на этапе Pull Request, а не в продакшене. Провайдер видел, что его изменения ломают конкретного потребителя, и мог либо скорректировать API, либо согласовать изменение с командой потребителя. Контрактное тестирование не заменило end-to-end тесты, но значительно снизило количество интеграционных сбоев.