Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Contract Testing: Consumer-Driven Contracts
contract_testing_consumer_driven

Contract Testing: Consumer-Driven Contracts

Что такое контрактное тестирование. Consumer-Driven Contracts. Pact или JSON Schema. Защита от breaking changes

Contract Testing проверяет что Provider (Backend) и Consumer (Frontend) согласованы в формате API. Ловит breaking changes до production.

Цель: Научиться создавать контракты между Frontend и Backend для защиты от несовместимых изменений.

Проблема: Breaking Changes в API

# Backend изменил API (breaking change) # Было: @app.get("/api/todos/{todo_id}") def get_todo(todo_id: int): return {"id": todo_id, "title": "Buy milk", "completed": false} # Стало: @app.get("/api/todos/{todo_id}") def get_todo(todo_id: int): return { "id": todo_id, "text": "Buy milk", # ❌ Переименовали title → text "status": "pending" # ❌ Переименовали completed → status }

Результат:

  • ✅ Backend тесты проходят (API работает)
  • ❌ Frontend ломается (ожидает "title", получает "text")
  • ❌ Узнаём об этом только в production!

Решение: Contract Testing

1. Consumer (Frontend) определяет ожидания:
   "GET /api/todos/1 должен вернуть {id, title, completed}"

2. Contract сохраняется

3. Provider (Backend) тестируется против контракта
   ✅ Если контракт нарушен → тесты падают ДО деплоя

Подход 1: JSON Schema

Consumer: Определить ожидания

# frontend/tests/contract_expectations.json { "endpoint": "GET /api/todos/{id}", "response_schema": { "type": "object", "required": ["id", "title", "completed"], "properties": { "id": {"type": "integer"}, "title": {"type": "string"}, "completed": {"type": "boolean"} } } }

Provider: Проверить контракт

# backend/tests/test_contract.py import jsonschema import json def test_get_todo_matches_contract(client): """Backend должен соответствовать контракту""" # Загрузить ожидания от Frontend with open("contracts/get_todo_schema.json") as f: schema = json.load(f) # Запрос к API response = client.get("/api/todos/1") # Валидация против контракта try: jsonschema.validate(response.json(), schema["response_schema"]) except jsonschema.ValidationError as e: pytest.fail(f"Contract violated: {e.message}")

Если Backend изменит API

pytest tests/test_contract.py # ❌ FAILED: Contract violated: 'title' is a required property

✅ Breaking change пойман ДО деплоя!

Подход 2: Pact (Industry Standard)

Установка

pip install pact-python

Consumer: Определить контракт

# frontend/tests/test_consumer_pact.py from pact import Consumer, Provider def test_get_todo_pact(): """Frontend ожидает определённый формат""" pact = Consumer('TodoFrontend').has_pact_with(Provider('TodoAPI')) # Определяем ожидание (pact .given('todo with ID 1 exists') .upon_receiving('a request for todo 1') .with_request('get', '/api/todos/1') .will_respond_with(200, body={ 'id': 1, 'title': 'Buy milk', 'completed': False })) with pact: # Frontend код использует API result = requests.get(pact.uri + '/api/todos/1') assert result.json()['title'] == 'Buy milk' # Pact file создан: pacts/todofrontend-todoapi.json

Provider: Проверить контракт

# backend/tests/test_provider_pact.py from pact import Verifier def test_provider_honors_contract(): """Backend проверяется против контракта от Frontend""" verifier = Verifier(provider='TodoAPI', provider_base_url="http://localhost:8000") # Верификация против pact file output, logs = verifier.verify_pacts('pacts/todofrontend-todoapi.json') assert output == 0 # Success

Если Backend нарушит контракт

pytest tests/test_provider_pact.py # ❌ FAILED: # Expected field 'title' in response # Actual response: {'id': 1, 'text': 'Buy milk', ...}

Consumer-Driven Contracts workflow

┌─────────────────┐
│   Frontend      │
│   (Consumer)    │
│                 │
│   1. Определяет │
│      ожидания   │
│      (Pact)     │
└────────┬────────┘
         │
         │ 2. Публикует Pact
         ↓
┌─────────────────┐
│   Pact Broker   │
│   (Shared repo) │
└────────┬────────┘
         │
         │ 3. Backend читает Pact
         ↓
┌─────────────────┐
│   Backend       │
│   (Provider)    │
│                 │
│   4. Проверяется│
│      против Pact│
│                 │
│   ✅ или ❌      │
└─────────────────┘

Проверьте свои знания

Вопросы ещё не добавлены

Вопросы для этой подтемы ещё не добавлены.

Далее: Практика: Контракты для Todo API