Документация Контрактное тестирование

Контрактное тестирование

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

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

Контракты — отчёт о дрейфе
Контракты — импорт Pact

Совет: Примеры кода доступны для cURL, CLI и SDK-клиентов (Go, Python, Java). Смотрите Руководство по SDK для установки и настройки. Смотрите Руководство по CLI для командной утилиты.

OpenAPI Spec Источник контракта Моки Mockarty Реальный API Движок контрактов Валидация моков Верификация провайдера Проверка совместимости Мульти-протокольная Отчёт Нарушения + Критичность JSON Интеграция CI/CD валидация сравнение верификация

Зачем контрактное тестирование?

В микросервисных архитектурах команды зависят от API-контрактов. Когда контракты ломаются незаметно:

  • Моки возвращают данные, которых реальный API больше не предоставляет
  • Появляются новые обязательные поля без обновления потребителей
  • Типы ответов меняются и ломают десериализацию
  • Эндпоинты удаляются без предупреждения

Контрактное тестирование ловит эти проблемы до продакшена.

Три режима валидации

1. Валидация моков

Проверяет, что ответы моков Mockarty соответствуют OpenAPI-спецификации.

Что проверяется:

  • Обязательные поля присутствуют в ответах моков
  • Типы полей соответствуют схеме (string, integer, array и т.д.)
  • Значения enum валидны
  • Паттерны строк и ограничения формата (email, UUID, date)
  • Ограничения min/max длины и числовых значений
  • Структура вложенных объектов

Поддержка шаблонов: Шаблонные выражения Mockarty ($.fake.Email, $.req.userId) обнаруживаются и пропускаются при проверке типов — выполняется только структурная валидация шаблонных ответов.

Использование:

  1. Перейдите в Contract Testing → вкладка Validate Mocks
  2. Вставьте OpenAPI-спецификацию (JSON или YAML) или укажите URL
  3. При необходимости отфильтруйте по тегам
  4. Нажмите 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-спецификации.

Что проверяется:

  • Коды ответов документированы в спецификации
  • Тело ответа соответствует схеме для каждого статус-кода
  • Все документированные эндпоинты доступны

Использование:

  1. Перейдите в Contract Testing → вкладка Verify Provider
  2. Укажите OpenAPI-спецификацию
  3. Введите базовый URL реального API (например, http://localhost:8080)
  4. При необходимости добавьте заголовки авторизации
  5. Нажмите 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-методы
  • Удалённые поля ответов (потребители от них зависят)
  • Новые обязательные параметры запроса или поля тела
  • Изменения типов (например, stringinteger)
  • Изменения nullable → non-nullable

Не-ломающие изменения, о которых сообщается:

  • Новые эндпоинты (аддитивные)
  • Новые необязательные поля
  • Удалённые параметры (не ломающие для потребителей)

Использование:

  1. Перейдите в Contract Testing → вкладка Compatibility
  2. Вставьте старую (базовую) спецификацию слева
  3. Вставьте новую (предлагаемую) спецификацию справа
  4. Нажмите 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 может уведомить вашу команду через настроенные каналы уведомлений.

Настройка уведомлений

  1. Настройте хотя бы один канал уведомлений в Админ → Каналы уведомлений (Slack, Telegram, Email, Teams, Discord и др.)
  2. Создайте конфиг контрактного тестирования на странице Contract Testing с cron-расписанием
  3. Когда запланированная валидация обнаруживает ошибки, отправляется уведомление с:
    • Количеством найденных нарушений
    • Списком ломающих изменений (если есть)
    • Ссылкой на полный отчёт в 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-спецификации. Другие команды могут подписаться на конкретные эндпоинты, от которых зависят, и получать автоматические уведомления при появлении ломающих изменений.

Как это работает

  1. Публикация: Каждая команда публикует свою API-спецификацию (OpenAPI, gRPC, GraphQL, MCP) в общий реестр
  2. Подписка: Другие команды подписываются на эндпоинты, которые используют, указывая какие части критичны
  3. Мониторинг: Планировщик периодически проверяет спецификации на изменения и запускает проверки совместимости
  4. Оповещение: Когда ломающие изменения затрагивают подписчиков, они получают уведомления автоматически
  5. Генерация: Команды могут генерировать моки напрямую из записей реестра одним кликом

Публикация 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 (схемы, типы, обязательные поля) Тестирует поведение системы при отказах (убийство подов, сетевые разделения)
Обнаруживает ломающие изменения до деплоя Обнаруживает хрупкую обработку ошибок под нагрузкой
Статический анализ контрактов Динамическое внедрение сбоев в работающих средах

Совместный рабочий процесс

  1. Перед деплоем: Запустите контрактную валидацию, чтобы убедиться, что моки соответствуют спецификациям и нет ломающих изменений
  2. После деплоя: Запустите хаос-эксперименты, чтобы проверить, что сервис корректно обрабатывает отказы
  3. Непрерывно: Настройте расписание для контрактной валидации и хаос-экспериментов по 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"

Возвращает статус-светофор (зелёный/жёлтый/красный) по каждому контракту на основе доступности провайдеров, изменений спецификаций и результатов верификации.

Смотрите также