Зачем контрактное тестирование?
В микросервисах сервисы разрабатываются и развёртываются независимо. Без контрактного тестирования вы полагаетесь на интеграционные тесты (медленные и хрупкие), документацию (может быть устаревшей) или удачу.
Контрактное тестирование проверяет, что сервисы могут корректно взаимодействовать, не требуя одновременной работы всех сервисов.
Как работает Pact
Pact использует consumer-driven подход: потребитель определяет ожидания, Pact генерирует контракт, провайдер его верифицирует.
Написание тестов потребителя
const { PactV3, MatchersV3 } = require('@pact-foundation/pact');
const { like, string, integer } = MatchersV3;
const provider = new PactV3({
consumer: 'OrderService',
provider: 'UserService',
});
describe('Контракт UserService', () => {
test('получение пользователя по ID', async () => {
await provider
.given('пользователь с ID 123 существует')
.uponReceiving('запрос пользователя 123')
.withRequest({ method: 'GET', path: '/users/123' })
.willRespondWith({
status: 200,
body: {
id: integer(123),
name: string('Alice'),
email: string('alice@example.com'),
},
});
await provider.executeTest(async (mockServer) => {
const userClient = new UserClient(mockServer.url);
const user = await userClient.getUser(123);
expect(user.id).toBe(123);
});
});
});
Написание тестов провайдера
const { Verifier } = require('@pact-foundation/pact');
describe('Верификация провайдера UserService', () => {
test('верифицирует все контракты', async () => {
const verifier = new Verifier({
providerBaseUrl: 'http://localhost:3001',
pactBrokerUrl: 'https://your-broker.pact.io',
provider: 'UserService',
stateHandlers: {
'пользователь с ID 123 существует': async () => {
await db.users.create({ id: 123, name: 'Alice', email: 'alice@example.com' });
},
},
});
await verifier.verifyProvider();
});
});
Матчеры Pact
Матчеры делают контракты гибкими: like() для типа, string() для строк, integer() для целых, eachLike() для массивов, regex() для паттернов.
Pact Broker
Центральный репозиторий контрактов. Команда can-i-deploy отвечает: «безопасно ли развернуть эту версию?»
Упражнение: Реализация контрактного тестирования
Задание 1: Контрактные тесты потребителя
Напишите тесты для OrderService, потребляющего ProductService API с 4 взаимодействиями.
Задание 2: Верификация провайдера
Напишите тесты провайдера с state handler-ами для каждого состояния “given”.
Задание 3: Интеграция с Pact Broker
Настройте локальный Pact Broker, публикуйте контракты, настройте верификацию, используйте can-i-deploy.
Задание 4: Обнаружение ломающих изменений
Внесите ломающее изменение, запустите верификацию, задокументируйте ошибку, исправьте.
Задание 5: Интеграция в CI Pipeline
Спроектируйте pipeline: Consumer CI публикует контракты, Provider CI верифицирует.
Результаты
- Код тестов потребителя минимум с 4 взаимодействиями.
- Код верификации провайдера с state handler-ами.
- Настройка Pact Broker.
- Документ дизайна CI pipeline.
- Демонстрация обнаружения ломающих изменений.