Тесты на контракт (contract testing) – это методика проверки взаимодействия между сервисами, когда одна сторона (поставщик) обязуется предоставить определённый API, а другая (потребитель) – использовать его согласно договорённостям. Правильный подход к сдаче таких тестов позволяет избежать дорогостоящих багов в продакшене и ускорить процесс интеграции.
Что такое тесты на контракт и зачем они нужны
Контрактный тест – это автоматизированный тест, который проверяет, что:
- Поставщик API возвращает ответы, соответствующие описанному контракту.
- Потребитель корректно формирует запросы, ожидая определённый формат ответа.
Основные преимущества:
- Раннее обнаружение несовместимостей между сервисами.
- Сокращение количества интеграционных тестов.
- Упрощение процесса CI/CD.
Подготовка к сдаче тестов
Выбор инструмента
Существует несколько популярных решений:
- Pact – для микросервисов, поддерживает Java, JavaScript, Ruby, Go и др.
- Spring Cloud Contract – интегрируется с Spring‑Boot.
- Postman/Newman – удобно для простых HTTP‑контрактов.
- OpenAPI (Swagger) + Dredd – проверка соответствия спецификации OpenAPI.
Описание контракта
Контракт обычно описывается в одном из форматов:
- JSON‑Schema
- YAML (OpenAPI)
- Pact DSL (Domain Specific Language)
Важно, чтобы контракт был однозначным и полным – включал все обязательные поля, типы данных, диапазоны значений и возможные коды ошибок.
Настройка окружения
Для успешного прохождения тестов подготовьте:
- Локальный или тестовый сервер поставщика, где реализованы все эндпоинты.
- Тестовые данные (fixtures) – набор запросов и ожидаемых ответов.
- CI‑pipeline (GitHub Actions, GitLab CI, Jenkins) с шагом «Run contract tests».
Пошаговый процесс сдачи тестов
Сбор требований
Сначала уточните у заказчика (или команды разработки) какие именно API‑контракты необходимо покрыть. Запишите:
- URL и HTTP‑метод.
- Структуру запроса (заголовки, тело).
- Ожидаемый ответ (статус‑код, тело, заголовки).
Создание контракта
Пример на Pact (JavaScript):
const { pactWith } = require('jest-pact');
pactWith({ consumer: 'MyApp', provider: 'UserService' }, provider => {
describe('User API', => {
beforeEach( => provider.addInteraction({
state: 'user exists',
uponReceiving: 'a request for user data',
withRequest: {
method: 'GET',
path: '/users/123',
headers: { Accept: 'application/json' }
},
willRespondWith: {
status: 200,
headers: { 'Content-Type': 'application/json' },
body: {
id: 123,
name: like('John Doe'),
email: regex('.+@.+\..+', 'john@example.com')
}
}
}));
// Тест, который отправляет запрос к мок‑серверу
it('returns user data', async => {
const response = await fetch('http://localhost:1234/users/123');
const data = await response.json;
expect(data.id).toBe(123);
});
});
});
Запуск тестов локально
Перед отправкой в репозиторий убедитесь, что все тесты проходят:
- Запустите мок‑сервер (Pact Mock Service).
- Выполните
npm testили аналогичную команду. - Проверьте, что сгенерированный
pact-файл соответствует ожиданиям.
Интеграция в CI/CD
Пример шага в GitHub Actions:
name: Contract Tests
on: [push, pull_request]
jobs:
contract-test:
runs-on: ubuntu-latest
steps:
⎯ uses: actions/checkout@v3
⸺ name: Set up Node
uses: actions/setup-node@v3
with:
node-version: '20'
⎯ name: Install dependencies
run: npm ci
⎯ name: Run Pact contract tests
run: npm run test:contract
⎯ name: Publish pact to broker
if: success
run: npx pact-broker publish ./pacts --consumer-app-version ${{ github.sha }}
Валидация со стороны поставщика
После того как потребитель опубликовал контракт в Pact Broker (или аналог), поставщик должен выполнить verification:
- Скачать контракт из брокера.
- Запустить тесты валидации против реального сервера поставщика.
- Если проверка не прошла – исправить реализацию API.
Частые ошибки и как их избежать
- Неполный контракт. Оставьте только «happy path» – тесты упадут, когда появятся новые поля.
- Жёстко зашитые данные. Используйте генераторы (like, eachLike, term) вместо фиксированных значений.
- Отсутствие версии контракта. Присваивайте каждому контракту семантическую версию (1.0.0).
- Тесты работают только локально. Настройте CI, иначе команда не увидит падения.
- Неправильный порядок публикации. Сначала публикуйте контракт, потом проверяйте поставщика.
Лучшие практики
- Контракт как код. Храните файлы
.pactв репозитории рядом с тестами. - Автоматическая публикация. При каждом успешном запуске CI публикуйте контракт в брокер.
- Версионирование. При изменении API создавайте новую версию контракта, а не перезаписывайте старую.
- Обратная совместимость. При добавлении новых полей делайте их опциональными, чтобы старые потребители не ломались.
- Документирование. Генерируйте README из контракта (pact‑doc, swagger‑ui) – это упрощает onboarding.
Пример полного рабочего цикла
Ниже схематично показан процесс от написания контракта до выпуска в продакшн:
Потребитель пишет контракт → commit → CI → публикация в Pact Broker
Поставщик получает уведомление (webhook) → скачивает контракт → запускает verification
Если verification прошёл → мерджит изменения → деплой новой версии API
Потребитель в следующем CI‑проекте снова проверяет, что его тесты проходят против новой версии
Сдача тестов на контракт – это не просто набор проверок, а целая культура взаимодействия между командами. Следуя описанному выше алгоритму, вы сможете:
- Сократить время на поиск интеграционных багов.
- Повысить доверие к микросервисной архитектуре.
- Автоматизировать процесс выпуска новых версий без риска поломать чужие сервисы.
Помните, что ключ к успеху – это чётко описанный контракт, автоматизированные проверки и постоянная коммуникация между поставщиком и потребителем.
Дата публикации: