Контрактное тестирование
Статус: Бета — Функция полностью работоспособна, но может получить изменения в будущих релизах. Включите в Настройки пользователя → «Включить контрактное тестирование».
Контрактное тестирование проверяет, что ваши моки, реализации API и спецификации остаются синхронизированными. Оно обнаруживает дрейф моков (когда моки расходятся со спецификацией), ломающие изменения (когда обновления спецификации ломают потребителей) и нарушения провайдера (когда реальный API не соответствует контракту).


Совет: Примеры кода доступны для cURL, CLI и SDK-клиентов (Go, Python, Java). Смотрите Руководство по SDK для установки и настройки. Смотрите Руководство по CLI для командной утилиты.
Зачем контрактное тестирование?
В микросервисных архитектурах команды зависят от API-контрактов. Когда контракты ломаются незаметно:
- Моки возвращают данные, которых реальный API больше не предоставляет
- Появляются новые обязательные поля без обновления потребителей
- Типы ответов меняются и ломают десериализацию
- Эндпоинты удаляются без предупреждения
Контрактное тестирование ловит эти проблемы до продакшена.
Три режима валидации
1. Валидация моков
Проверяет, что ответы моков Mockarty соответствуют OpenAPI-спецификации.
Что проверяется:
- Обязательные поля присутствуют в ответах моков
- Типы полей соответствуют схеме (string, integer, array и т.д.)
- Значения enum валидны
- Паттерны строк и ограничения формата (email, UUID, date)
- Ограничения min/max длины и числовых значений
- Структура вложенных объектов
Поддержка шаблонов: Шаблонные выражения Mockarty ($.fake.Email, $.req.userId) обнаруживаются и пропускаются при проверке типов — выполняется только структурная валидация шаблонных ответов.
Использование:
- Перейдите в Contract Testing → вкладка Validate Mocks
- Вставьте OpenAPI-спецификацию (JSON или YAML) или укажите URL
- При необходимости отфильтруйте по тегам
- Нажмите Run Validation
API:
cURL
curl -X POST http://localhost:5770/api/v1/contract/validate-mocks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"specContent": "...",
"namespace": "default"
}'
CLI
mockarty-cli contract validate --spec openapi.yaml --namespace default
Go
// Валидация моков против спецификации
result, err := client.Contracts().ValidateMocks(ctx, &mockarty.ValidateMocksRequest{
SpecContent: "...",
Namespace: "default",
})
Python
# Валидация моков против спецификации
result = client.contracts.validate_mocks(spec_content="...", namespace="default")
Java
// Валидация моков против спецификации
var result = client.contracts().validateMocks("...", "default");
2. Верификация провайдера
Отправляет реальные HTTP-запросы к вашему API и проверяет, что ответы соответствуют OpenAPI-спецификации.
Что проверяется:
- Коды ответов документированы в спецификации
- Тело ответа соответствует схеме для каждого статус-кода
- Все документированные эндпоинты доступны
Использование:
- Перейдите в Contract Testing → вкладка Verify Provider
- Укажите OpenAPI-спецификацию
- Введите базовый URL реального API (например,
http://localhost:8080) - При необходимости добавьте заголовки авторизации
- Нажмите Run Verification
API:
cURL
curl -X POST http://localhost:5770/api/v1/contract/verify-provider \
-H "Content-Type: application/json" \
-d '{
"specUrl": "https://api.example.com/openapi.json",
"baseUrl": "http://localhost:8080",
"headers": {"Authorization": "Bearer token123"}
}'
CLI
mockarty-cli contract verify --spec https://api.example.com/openapi.json \
--base-url http://localhost:8080 \
--header "Authorization: Bearer token123"
Go
// Верификация провайдера против спецификации
result, err := client.Contracts().VerifyProvider(ctx, &mockarty.VerifyProviderRequest{
SpecURL: "https://api.example.com/openapi.json",
BaseURL: "http://localhost:8080",
Headers: map[string]string{"Authorization": "Bearer token123"},
})
Python
# Верификация провайдера против спецификации
result = client.contracts.verify_provider(
spec_url="https://api.example.com/openapi.json",
base_url="http://localhost:8080",
headers={"Authorization": "Bearer token123"},
)
Java
// Верификация провайдера против спецификации
var result = client.contracts().verifyProvider(
"https://api.example.com/openapi.json",
"http://localhost:8080",
Map.of("Authorization", "Bearer token123")
);
3. Проверка обратной совместимости
Сравнивает две версии спецификации API и обнаруживает ломающие изменения.
Обнаруживаемые ломающие изменения:
- Удалённые эндпоинты или HTTP-методы
- Удалённые поля ответов (потребители от них зависят)
- Новые обязательные параметры запроса или поля тела
- Изменения типов (например,
string→integer) - Изменения nullable → non-nullable
Не-ломающие изменения, о которых сообщается:
- Новые эндпоинты (аддитивные)
- Новые необязательные поля
- Удалённые параметры (не ломающие для потребителей)
Использование:
- Перейдите в Contract Testing → вкладка Compatibility
- Вставьте старую (базовую) спецификацию слева
- Вставьте новую (предлагаемую) спецификацию справа
- Нажмите Run Check
API:
cURL
curl -X POST http://localhost:5770/api/v1/contract/check-compatibility \
-H "Content-Type: application/json" \
-d '{
"oldSpecContent": "...",
"newSpecContent": "..."
}'
CLI
mockarty-cli contract check-compatibility --old old-spec.yaml --new new-spec.yaml
Go
// Проверка обратной совместимости
result, err := client.Contracts().CheckCompatibility(ctx, &mockarty.CheckCompatibilityRequest{
OldSpecContent: "...",
NewSpecContent: "...",
})
Python
# Проверка обратной совместимости
result = client.contracts.check_compatibility(
old_spec_content="...",
new_spec_content="...",
)
Java
// Проверка обратной совместимости
var result = client.contracts().checkCompatibility("...", "...");
Мультипротокольная поддержка
Контрактное тестирование работает с несколькими протоколами:
| Протокол | Формат спецификации | Что проверяется |
|---|---|---|
| HTTP/REST | OpenAPI/Swagger | Схемы ответов, статус-коды, обязательные поля, типы |
| GraphQL | GraphQL Schema (SDL/introspection) | Существование полей query/mutation, соответствие типов возвращаемых значений |
| gRPC | Определения сервисов | Существование сервисов и методов в proto |
| SOAP | Определения операций | Существование операций в WSDL |
| MCP | Определения MCP-инструментов | Существование инструментов и ресурсов, соответствие схемам |
| AsyncAPI | AsyncAPI Spec | Валидация каналов и схем сообщений |
Форматы экспорта
Результаты контрактного тестирования можно экспортировать для интеграции с CI/CD:
| Формат | Применение | Content-Type |
|---|---|---|
| JSON | CI/CD интеграция, обработка, хранение | application/json |
Интеграция с CI/CD
GitHub Actions
- name: Contract Validation
run: |
RESULT=$(curl -s -o /tmp/contract-report.xml -w "%{http_code}" \
-X POST $MOCKARTY_URL/api/v1/contract/validate-mocks \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $MOCKARTY_TOKEN" \
-d "{\"specUrl\": \"$SPEC_URL\", \"exportFormat\": \"json\"}")
if [ "$RESULT" != "200" ]; then
echo "Contract validation request failed"
exit 1
fi
- name: Publish Test Results
uses: dorny/test-reporter@v1
with:
name: Contract Tests
path: /tmp/contract-report.xml
reporter: json
GitLab CI
contract-test:
script:
- curl -s -X POST "$MOCKARTY_URL/api/v1/contract/check-compatibility"
-H "Content-Type: application/json"
-d "{\"oldSpecContent\": \"$(cat old-spec.json)\", \"newSpecContent\": \"$(cat new-spec.json)\", \"exportFormat\": \"json\"}"
-o contract-results.xml
artifacts:
reports:
json: contract-results.json
Интеграция с AI-агентом
Суб-агент контрактного тестирования доступен из Agent Chat:
- «Проверь мои моки против спецификации OpenAPI»
- «Проверь обратную совместимость между этими двумя спецификациями»
- «Верифицируй, что наш API соответствует спецификации»
- «Есть ли ломающие изменения в новой версии API?»
Справочник по результатам проверки
| Тип нарушения | Серьёзность | Описание |
|---|---|---|
missing_required |
error | Обязательное поле отсутствует в ответе |
type_mismatch |
error | Тип значения не соответствует схеме |
enum_violation |
error | Значение не входит в допустимый список enum |
constraint_violation |
error | Нарушено ограничение min/max/pattern |
removed_endpoint |
error | Эндпоинт удалён (ломающее изменение) |
removed_field |
error | Поле ответа удалено (ломающее изменение) |
added_required |
error | Новый обязательный параметр (ломающее изменение) |
type_change |
error | Тип поля изменён (ломающее изменение) |
undocumented_endpoint |
warning | Эндпоинт мока отсутствует в спецификации |
undocumented_status |
warning | Статус-код не документирован |
format_violation |
warning | Формат строки не соответствует ожидаемому |
additional_property |
warning | Лишнее поле, отсутствующее в схеме |
added_endpoint |
info | Добавлен новый эндпоинт (не-ломающее изменение) |
Быстрый старт: 3 шага к контрактному тестированию
Начать работу можно менее чем за 5 минут:
Шаг 1: Включите функцию
Перейдите в Настройки пользователя (иконка шестерёнки) → установите галочку «Включить контрактное тестирование». Пункт «Contracts» появится в боковом меню.
Шаг 2: Запустите первую валидацию
Откройте страницу Contract Testing → вставьте OpenAPI-спецификацию → нажмите «Run Validation». Вы мгновенно увидите, какие моки расходятся с контрактом.
Шаг 3: Добавьте в CI/CD
Скопируйте curl-команду из документации и добавьте в свой пайплайн. Результаты возвращаются в формате JSON для интеграции с вашими CI/CD инструментами.
Практические сценарии использования
Для QA-инженеров
- Перед релизом: запустите валидацию моков, чтобы убедиться, что тестовые дубли соответствуют актуальной спецификации API
- После обновления API: запустите проверку обратной совместимости, чтобы обнаружить ломающие изменения до того, как они дойдут до потребителей
- Регрессионное тестирование: настройте периодическую верификацию провайдера по расписанию, чтобы обнаруживать дрейф API на ранней стадии
Для разработчиков
- В процессе разработки: валидируйте ответы моков при их создании — мгновенно выявляйте несоответствия схеме
- В pull requests: добавьте контрактные проверки в CI — блокируйте мерж при ломающих изменениях
- API-first подход: сначала пишите спецификацию, затем валидируйте, что моки и реализация ей соответствуют
Для DevOps / SRE
- Непрерывный мониторинг: настройте расписание контрактной валидации каждые 6 часов — получайте уведомления при обнаружении дрейфа
- Мульти-окружение: запускайте верификацию провайдера на staging и production для сравнения
- Аудит: вкладка «History» показывает все запуски валидации с информацией о том, кто и когда их запускал
Уведомления
Когда запланированная контрактная валидация обнаруживает проблемы, Mockarty может уведомить вашу команду через настроенные каналы уведомлений.
Настройка уведомлений
- Настройте хотя бы один канал уведомлений в Админ → Каналы уведомлений (Slack, Telegram, Email, Teams, Discord и др.)
- Создайте конфиг контрактного тестирования на странице Contract Testing с cron-расписанием
- Когда запланированная валидация обнаруживает ошибки, отправляется уведомление с:
- Количеством найденных нарушений
- Списком ломающих изменений (если есть)
- Ссылкой на полный отчёт в UI Mockarty
Интеграция через Webhook
Вы также можете использовать систему Webhook Mockarty для запуска внешних действий при провале контрактных тестов:
# Пример: проверить контракт и отправить результат в Slack
RESULT=$(curl -s -X POST "$MOCKARTY_URL/api/v1/contract/validate-mocks" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"specUrl": "https://api.example.com/spec.json"}')
ERRORS=$(echo "$RESULT" | jq '.summary.errors')
if [ "$ERRORS" -gt "0" ]; then
curl -X POST "$SLACK_WEBHOOK" \
-d "{\"text\": \"Contract validation failed: $ERRORS errors found\"}"
fi
Какую пользу контрактное тестирование приносит вашему продукту
| Без контрактного тестирования | С контрактным тестированием |
|---|---|
| Моки незаметно расходятся с реальным API | Мгновенное уведомление при дрейфе мока от спецификации |
| Ломающие изменения обнаруживаются в продакшене | Ломающие изменения обнаруживаются в CI до мержа |
| Ручная проверка спецификаций в pull requests | Автоматическая проверка обратной совместимости |
| «Работает на моей машине» с устаревшими моками | Непрерывная валидация обеспечивает актуальность моков |
| Часы отладки интеграционных сбоев | Точный диагноз: «поле X удалено из ответа» |
Реестр API (внутренний маркетплейс)
Реестр API – это внутренний маркетплейс, где команды публикуют свои API-спецификации. Другие команды могут подписаться на конкретные эндпоинты, от которых зависят, и получать автоматические уведомления при появлении ломающих изменений.
Как это работает
- Публикация: Каждая команда публикует свою API-спецификацию (OpenAPI, gRPC, GraphQL, MCP) в общий реестр
- Подписка: Другие команды подписываются на эндпоинты, которые используют, указывая какие части критичны
- Мониторинг: Планировщик периодически проверяет спецификации на изменения и запускает проверки совместимости
- Оповещение: Когда ломающие изменения затрагивают подписчиков, они получают уведомления автоматически
- Генерация: Команды могут генерировать моки напрямую из записей реестра одним кликом
Публикация API
cURL
curl -X POST "$MOCKARTY_URL/api/v1/contract/registry" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"serviceName": "UserService",
"specType": "openapi",
"version": "2.1.0",
"description": "User management API",
"visibility": "public",
"specUrl": "https://api.internal.com/user-service/openapi.json"
}'
Go
entry, err := client.Contracts().PublishToRegistry(ctx, &mockarty.RegistryEntry{
ServiceName: "UserService",
SpecType: "openapi",
Version: "2.1.0",
Description: "User management API",
Visibility: "public",
SpecURL: "https://api.internal.com/user-service/openapi.json",
})
Python
entry = await client.contracts.publish_to_registry(
service_name="UserService",
spec_type="openapi",
version="2.1.0",
description="User management API",
visibility="public",
spec_url="https://api.internal.com/user-service/openapi.json",
)
Java
var entry = client.contracts().publishToRegistry(Map.of(
"serviceName", "UserService",
"specType", "openapi",
"version", "2.1.0",
"description", "User management API",
"visibility", "public",
"specUrl", "https://api.internal.com/user-service/openapi.json"
));
Поддерживаемые типы спецификаций: openapi, grpc, graphql, mcp, asyncapi, wsdl
Уровни видимости:
- public – видно всем пространствам имён
- internal – видно внутри организации
- restricted – видно только публикующему пространству имён
Подписка на API
cURL
curl -X POST "$MOCKARTY_URL/api/v1/contract/registry/{id}/subscribe" \
-H "Content-Type: application/json" \
-d '{
"serviceName": "OrderService",
"watchEndpoints": ["GET /api/users/:id", "GET /api/users"],
"notifyOnBreaking": true,
"autoBlock": false
}'
Go
sub, err := client.Contracts().Subscribe(ctx, registryEntryID, &mockarty.Subscription{
ServiceName: "OrderService",
WatchEndpoints: []string{"GET /api/users/:id", "GET /api/users"},
NotifyOnBreaking: true,
})
Python
sub = await client.contracts.subscribe(
registry_entry_id=entry_id,
service_name="OrderService",
watch_endpoints=["GET /api/users/:id", "GET /api/users"],
notify_on_breaking=True,
)
Java
var sub = client.contracts().subscribe(entryId, Map.of(
"serviceName", "OrderService",
"watchEndpoints", List.of("GET /api/users/:id", "GET /api/users"),
"notifyOnBreaking", true
));
Проверка влияния перед публикацией изменений
Перед обновлением API проверьте, кого это затронет:
curl -X POST "$MOCKARTY_URL/api/v1/contract/registry/{id}/check-impact" \
-H "Content-Type: application/json" \
-d '{"newSpecContent": "..."}'
Ответ:
{
"isCompatible": false,
"breakingChanges": 2,
"changedEndpoints": ["GET /api/users/:id"],
"affectedTeams": [
{"namespace": "commerce", "serviceName": "OrderService", "autoBlock": false}
],
"blocked": false
}
Генерация моков из реестра
Превратите любой опубликованный API в моки одним кликом:
curl -X POST "$MOCKARTY_URL/api/v1/contract/registry/{id}/generate-mocks" \
-H "Authorization: Bearer $TOKEN"
Моки создаются с тегами registry, source:registry, service:Name, team:Namespace.
Автоматический мониторинг спецификаций
Планировщик периодически проверяет записи реестра с URL на наличие изменений:
- Обнаруживает модификации спецификаций через сравнение SHA256-хешей
- Запускает проверку обратной совместимости при обнаружении изменений
- Уведомляет затронутых подписчиков о ломающих изменениях
- Обрабатывает не более 10 записей за цикл для предотвращения перегрузки
Контракты потребителей (Consumer-Driven Contracts)
Реализация паттерна consumer-driven contract testing, популяризированного Pact.
Consumer-Driven Contracts переворачивают модель тестирования: вместо того, чтобы провайдер определял контракт, каждый потребитель публикует свои ожидания. Провайдер затем верифицирует реализацию против всех контрактов потребителей.
Почему consumer-driven?
| Спецификация как контракт (OpenAPI) | Consumer-Driven (Pact) |
|---|---|
| Провайдер описывает полную спецификацию | Потребители описывают только то, что им нужно |
| Изменения проверяются вручную | Изменения автоматически верифицируются против всех потребителей |
| Нет проверки безопасности деплоя | «Можно ли деплоить?» блокирует небезопасные деплои |
| Требуется полная спецификация заранее | Минимальные контракты, развиваются постепенно |
Рабочий процесс
1. Потребитель генерирует Pact JSON из теста
2. CI потребителя публикует Pact в Mockarty: POST /api/v1/contract/pacts
3. CI провайдера запускает верификацию: POST /api/v1/contract/pacts/verify
4. Перед деплоем: POST /api/v1/contract/pacts/can-i-deploy
5. (Опционально) Автогенерация моков из взаимодействий Pact
Публикация контракта
cURL
curl -X POST "$MOCKARTY_URL/api/v1/contract/pacts" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d "{\"pactContent\": $(cat pact.json | jq -Rs .), \"version\": \"$GIT_SHA\"}"
Go
// Публикация Pact-контракта
pact, err := client.Contracts().PublishPact(ctx, &mockarty.PublishPactRequest{
PactContent: string(pactJSON),
Version: gitSHA,
})
Python
# Публикация Pact-контракта
pact = client.contracts.publish_pact(pact_content=pact_json, version=git_sha)
Java
// Публикация Pact-контракта
var pact = client.contracts().publishPact(pactJson, gitSha);
Формат файла Pact
Mockarty принимает все три версии спецификации, выпускаемые экосистемой
pact-foundation: v2 (устаревшие inline matching rules), v3
(текущий стабильный формат) и v4 (бинарно-безопасные envelope для
тел и вложенные категории matching rules). Все три формы нормализуются
на приёме, поэтому клиентские библиотеки — pact-js, pact-python,
pact-jvm, pact-rust, pact-go — могут публиковать напрямую без
конвертации.
Помимо HTTP-взаимодействий Mockarty верифицирует message-взаимодействия
из v4-пактов — как Asynchronous/Messages, так и Synchronous/Messages.
Асинхронные сообщения верифицируются отправкой POST-запроса с
конвертом {description, providerStates, requestContents} на
колбэк-URL провайдера; поля contents и metadata из ответа
сопоставляются с пактом тем же матчером, что и HTTP-тела. Синхронные
сообщения дополнительно сравнивают ответ колбэка с первым вариантом
response[] из пакта. Message- и HTTP-взаимодействия могут
сосуществовать в одном пакте — Mockarty отправляет каждое в
соответствующий верификатор и возвращает агрегированный результат.
Пример v3:
{
"consumer": { "name": "OrderService" },
"provider": { "name": "UserService" },
"interactions": [
{
"description": "get user by ID",
"request": { "method": "GET", "path": "/api/users/123" },
"response": {
"status": 200,
"body": { "id": 123, "name": "John" },
"matchingRules": {
"$.body.id": { "matchers": [{ "match": "type" }] },
"$.body.name": { "matchers": [{ "match": "type" }] }
}
}
}
]
}
Правила сопоставления
Mockarty реализует полный каталог матчеров pact-reference. Для скалярных
матчеров правило терминально: значение в этой точке валидируется, и
рекурсия прекращается. Для type / min / max, применённых к
композиту (объект или массив), верхний уровень проверяется, а дочерние
пути всё равно обходятся, чтобы сработали вложенные правила.
| Правило | Описание | Пример |
|---|---|---|
equality |
Точное совпадение значения (по умолчанию, если match не указан) |
{"match": "equality", "value": 42} |
type |
Совпадение JSON-типа с шаблонным значением | {"match": "type"} |
regex |
Строка соответствует шаблону; пустой шаблон отклоняется | {"match": "regex", "regex": "^[A-Z]{2}\\d+$"} |
integer |
Число без дробной части | {"match": "integer"} |
decimal |
Любое число | {"match": "decimal"} |
number |
Любое число (int или float) | {"match": "number"} |
boolean |
true или false (bool НЕ считается числом) |
{"match": "boolean"} |
null / notNull |
Значение равно / не равно JSON null |
{"match": "notNull"} |
min |
Массив должен содержать не менее N элементов; каждый элемент валидируется по шаблону | {"match": "min", "min": 1} |
max |
Массив должен содержать не более N элементов | {"match": "max", "max": 100} |
include |
Строка содержит подстроку | {"match": "include", "value": "@"} |
date |
ISO-дата; пользовательский regex переопределяет стандарт | {"match": "date"} |
time |
HH:MM:SS[.fraction] |
{"match": "time"} |
timestamp / datetime |
RFC 3339 / ISO 8601 с опциональным offset | {"match": "timestamp"} |
uuid |
Каноническая форма UUID v4 | {"match": "uuid"} |
semver |
Версия SemVer 2.0 | {"match": "semver"} |
ipv4 |
IPv4 в формате dotted-quad | {"match": "ipv4"} |
contentType |
Префиксное сравнение строки — допускает параметры ; charset=... |
{"match": "contentType", "value": "application/json"} |
Некорректные или пустые шаблоны matcher-ов выдают mismatch при
верификации, а не тихо игнорируются — сломанное правило клиента не
должно превращаться в ложно-зелёный результат.
Верификация провайдера
cURL
curl -X POST "$MOCKARTY_URL/api/v1/contract/pacts/verify" \
-H "Content-Type: application/json" \
-d '{"pactId": "uuid", "providerBaseUrl": "http://localhost:8080"}'
Go
// Верификация провайдера против Pact-контракта
result, err := client.Contracts().VerifyPact(ctx, &mockarty.VerifyPactRequest{
PactID: "uuid",
ProviderBaseURL: "http://localhost:8080",
})
Python
# Верификация провайдера против Pact-контракта
result = client.contracts.verify_pact(pact_id="uuid", provider_base_url="http://localhost:8080")
Java
// Верификация провайдера против Pact-контракта
var result = client.contracts().verifyPact("uuid", "http://localhost:8080", Map.of());
Можно ли деплоить?
cURL
curl -X POST "$MOCKARTY_URL/api/v1/contract/pacts/can-i-deploy" \
-d '{"participant": "UserService"}'
CLI
mockarty-cli contract can-i-deploy --participant UserService
Go
// Проверка возможности деплоя
result, err := client.Contracts().CanIDeploy(ctx, "UserService")
Python
# Проверка возможности деплоя
result = client.contracts.can_i_deploy(participant="UserService")
Java
// Проверка возможности деплоя
var result = client.contracts().canIDeploy("UserService");
Ответ:
{
"deployable": true,
"summary": "UserService: all 3 pact(s) verified successfully",
"matrix": [
{"consumer": "OrderService", "provider": "UserService", "success": true},
{"consumer": "BillingService", "provider": "UserService", "success": true}
]
}
Автогенерация моков из контрактов
curl -X POST "$MOCKARTY_URL/api/v1/contract/pacts/{id}/generate-mocks"
Создаёт HTTP-моки для каждого взаимодействия с тегами pact, consumer:Name, provider:Name.
Обнаружение дрейфа
Сравнение ответов моков с реальным API для обнаружения расхождений:
cURL
curl -X POST http://localhost:5770/api/v1/contract/detect-drift \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"baseUrl": "http://localhost:8080",
"headers": {"Authorization": "Bearer test-token"}
}'
CLI
mockarty-cli contract detect-drift --base-url http://localhost:8080 \
--header "Authorization: Bearer test-token"
Go
// Обнаружение дрейфа моков от реального API
report, err := client.Contracts().DetectDrift(ctx, &mockarty.DetectDriftRequest{
BaseURL: "http://localhost:8080",
Headers: map[string]string{"Authorization": "Bearer test-token"},
})
Python
# Обнаружение дрейфа моков от реального API
report = client.contracts.detect_drift(
base_url="http://localhost:8080",
headers={"Authorization": "Bearer test-token"},
)
Java
// Обнаружение дрейфа моков от реального API
var report = client.contracts().detectDrift(
"http://localhost:8080",
Map.of("Authorization", "Bearer test-token")
);
Совместимость с Pact Broker v2/v3
Mockarty реализует полностью совместимый API Pact Broker, что позволяет использовать стандартные Pact-инструменты (pact-broker CLI, pact-js, pact-python, pact-jvm, pact-go) без изменения конфигурации.
Поддерживаемые эндпоинты
| Метод | Эндпоинт | Описание |
|---|---|---|
| PUT | /pacts/provider/{provider}/consumer/{consumer}/version/{version} |
Публикация контракта |
| GET | /pacts/provider/{provider}/consumer/{consumer}/version/{version} |
Получение контракта |
| GET | /pacts/provider/{provider}/consumer/{consumer}/latest |
Получение последнего контракта |
| GET | /pacts/provider/{provider}/consumer/{consumer}/latest/{tag} |
Получение последнего по тегу |
| DELETE | /pacts/provider/{provider}/consumer/{consumer}/version/{version} |
Удаление контракта |
| POST | /pacts/provider/{provider}/consumer/{consumer}/pact-version/{version}/verification-results |
Публикация результатов верификации |
| GET | /can-i-deploy?pacticipant={name}&version={version}&to={env} |
Проверка готовности к деплою (необязательный to фильтрует по тегу окружения) |
| GET | /pacticipants |
Список участников |
| PUT | /pacticipants/{name}/versions/{version}/tags/{tag} |
Тегирование версии |
| GET | /matrix?q[][pacticipant]={name} |
Матрица совместимости |
Использование с pact-broker CLI
# Указать URL брокера
export PACT_BROKER_BASE_URL=http://localhost:5770
# Опубликовать контракт
pact-broker publish ./pacts --consumer-app-version=1.0.0
# Можно ли деплоить? — форма с указанием окружения:
pact-broker can-i-deploy --pacticipant=MyService --version=1.0.0 --to=production
# Тегировать версию
pact-broker create-version-tag --pacticipant=MyService --version=1.0.0 --tag=production
Использование с pact-js
const { Publisher } = require('@pact-foundation/pact');
new Publisher({
pactBroker: 'http://localhost:5770',
pactFilesOrDirs: ['./pacts'],
consumerVersion: '1.0.0',
}).publishPacts();
Использование с pact-python
from pact import Broker
broker = Broker(broker_base_url='http://localhost:5770')
broker.publish('ConsumerApp', '1.0.0', pact_dir='./pacts')
Ограничения
- Шаблонные выражения: Ответы моков с шаблонными выражениями (
$.fake.*,$.req.*и т.д.) проверяются только структурно (обязательные поля). Проверки типов для шаблонных значений могут выдавать предупреждения - Хранение данных: Данные контрактного тестирования хранятся в базе данных. Убедитесь, что ваш экземпляр Mockarty настроен с подключённой базой данных
- Только HTTP для верификации провайдера: gRPC/GraphQL интроспекция запланирована
- Consumer-Driven Contracts: Принимается Pact v2, v3 и v4 JSON — все три формы нормализуются во внутренний плоский вид на приёме. Callback-и состояний провайдера опциональны. Верифицируются как HTTP-взаимодействия, так и message-взаимодействия (
Asynchronous/MessagesиSynchronous/Messages) из v4-пактов; последние — через HTTP-колбэк провайдера, задаваемый на каждый прогон верификации.
Интеграция с Chaos Testing
Контрактное тестирование и хаос-инженерия работают вместе, чтобы обеспечить корректность и отказоустойчивость ваших сервисов.
Как они дополняют друг друга
| Контрактное тестирование | Хаос-инженерия |
|---|---|
| Проверяет корректность API (схемы, типы, обязательные поля) | Тестирует поведение системы при отказах (убийство подов, сетевые разделения) |
| Обнаруживает ломающие изменения до деплоя | Обнаруживает хрупкую обработку ошибок под нагрузкой |
| Статический анализ контрактов | Динамическое внедрение сбоев в работающих средах |
Совместный рабочий процесс
- Перед деплоем: Запустите контрактную валидацию, чтобы убедиться, что моки соответствуют спецификациям и нет ломающих изменений
- После деплоя: Запустите хаос-эксперименты, чтобы проверить, что сервис корректно обрабатывает отказы
- Непрерывно: Настройте расписание для контрактной валидации и хаос-экспериментов по cron для обнаружения регрессий
Пример: пайплайн Contract + Chaos
# GitHub Actions: валидация контрактов, затем хаос-эксперименты
- name: Contract Validation
run: |
curl -s -X POST $MOCKARTY_URL/api/v1/contract/validate-mocks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"specUrl": "$SPEC_URL"}'
- name: Chaos Experiment (after deploy)
run: |
curl -s -X POST $MOCKARTY_URL/api/v1/chaos/experiments \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"preset": "network-partition", "target": {"namespace": "staging"}}'
Когда хаос-эксперимент показывает, что сервис отказывает под нагрузкой, используйте контрактное тестирование для проверки того, что ответы об ошибках по-прежнему соответствуют API-контракту – чтобы потребители могли корректно обрабатывать отказы.
Контракты потребителей (Пакеты зависимостей)
Контракты потребителей объявляют, от каких API провайдеров зависит ваш сервис, какие эндпоинты вы используете и какие поля критичны — как в запросах (что вы отправляете), так и в ответах (что вы читаете). Если провайдер удалит поле, от которого вы зависите, контракт это поймает до деплоя.
Контракт отслеживает три типа зависимостей по полям на каждый эндпоинт:
| Тип поля | Описание | Пример |
|---|---|---|
| Поля ответа | Поля, которые ваш сервис читает из ответа | $.user.email, $.order.total |
| Поля запроса | Поля, которые ваш сервис отправляет в теле запроса | $.body.email, $.body.password |
| Параметры | URL path, query и header параметры | $.param.id (path), $.param.limit (query) |
Поддерживаемые протоколы: OpenAPI/REST, gRPC, GraphQL, MCP, WSDL/SOAP, AsyncAPI.
Создание контракта потребителя
Используйте визуальный визард в UI (вкладка Контракты > Новый контракт), который проведёт вас через выбор провайдеров, эндпоинтов и настройку полей. Или API:
curl -X POST $MOCKARTY_URL/api/v1/contract/consumer-contracts \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "OrderService",
"dependencies": [
{
"registryEntryId": "user-service-api-id",
"providerName": "UserService",
"providerVersion": "latest",
"endpoints": [
{
"route": "GET /api/users/{id}",
"protocol": "openapi",
"expectedStatus": [200],
"requiredFields": [
{"path": "$.id", "type": "integer", "required": true},
{"path": "$.email", "type": "string", "required": true}
],
"requiredParameters": [
{"path": "$.param.id", "type": "string", "required": true}
]
},
{
"route": "PUT /api/users/{id}",
"protocol": "openapi",
"expectedStatus": [200],
"requiredFields": [
{"path": "$.id", "type": "string", "required": true}
],
"requiredRequestFields": [
{"path": "$.body.email", "type": "string", "required": true},
{"path": "$.body.name", "type": "string", "required": true}
],
"requiredParameters": [
{"path": "$.param.id", "type": "string", "required": true}
]
}
]
}
],
"tags": ["critical"]
}'
CLI
# Список контрактов потребителей
mockarty-cli contract consumer-contracts list
# Проверить контракт
mockarty-cli contract consumer-contracts check --id <contract-id>
# Двунаправленная проверка деплоя
mockarty-cli contract deploy-check --role consumer --contract-id <id>
mockarty-cli contract deploy-check --role provider --registry-id <id> --spec new-api.yaml
Можно деплоить? (V2 – Двунаправленная проверка)
Улучшенная проверка “Можно деплоить?” поддерживает две перспективы:
- Как потребитель: “Не сломают ли меня мои зависимости?” – проверяет контракт против текущих спецификаций провайдеров
- Как провайдер: “Не сломаю ли я других?” – проверяет новую спецификацию против всех контрактов потребителей
# Проверка как потребитель
curl -X POST $MOCKARTY_URL/api/v1/contract/can-i-deploy \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "consumer", "contractId": "order-service-contract-id"}'
# Проверка провайдера перед деплоем
curl -X POST $MOCKARTY_URL/api/v1/contract/can-i-deploy \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"role": "provider", "registryEntryId": "user-api-id", "newSpec": "{...}"}'
История версий API реестра
Записи реестра хранят историю версий (до 10 версий). Сравнивайте версии и откатывайте при необходимости.
# Список версий
curl $MOCKARTY_URL/api/v1/contract/registry/<id>/versions \
-H "Authorization: Bearer $TOKEN"
# Сравнить две версии
curl $MOCKARTY_URL/api/v1/contract/registry/<id>/versions/1/diff/2 \
-H "Authorization: Bearer $TOKEN"
# Откат к предыдущей версии
curl -X POST $MOCKARTY_URL/api/v1/contract/registry/<id>/versions/1/rollback \
-H "Authorization: Bearer $TOKEN"
Панель здоровья
Мониторинг здоровья всех контрактов в вашем пространстве имён:
curl $MOCKARTY_URL/api/v1/contract/health \
-H "Authorization: Bearer $TOKEN"
Возвращает статус-светофор (зелёный/жёлтый/красный) по каждому контракту на основе доступности провайдеров, изменений спецификаций и результатов верификации.
Смотрите также
- Справочник API – Полная документация REST API
- Хаос-инженерия – Внедрение сбоев и тестирование отказоустойчивости
- Фаззинг – Автоматизированное тестирование безопасности API
- Нагрузочное тестирование – Нагрузочное тестирование API с помощью JavaScript-скриптов