0.1.3 · блок 0
pytest и контракты
pytest и контракты
Зачем это нужно
Модель может выдавать правильный score, но сервис всё равно сломается из-за изменённого JSON-поля, status code или формата ошибки. Тесты проверяют не только алгоритм, но и обещания сервиса его потребителям.
Основные идеи
pytest ищет тесты, запускает их изолированно и показывает короткий отчёт о сбоях. Один тест отвечает на один понятный вопрос.
Fixtures готовят общие данные и зависимости: тестовый клиент, маленькую модель, настройки. Они уменьшают копирование и не должны скрывать важный контекст.
Contract test HTTP API фиксирует внешний договор:
| Сценарий | Ожидание |
|---|---|
| валидный запрос | `200`, предсказание нужного типа |
| нет обязательного поля | `422`, понятная ошибка валидации |
| модель недоступна | контролируемый `503`, не traceback |
Контракт — это не проверка внутренней реализации. После рефакторинга API-тест должен оставаться зелёным, если внешний договор не изменился.
Как это выглядит на практике
def test_predict_returns_probability(client):
response = client.post("/predict", json={"age": 35, "income": 120_000})
assert response.status_code == 200
body = response.json()
assert 0 <= body["probability"] <= 1
def test_predict_rejects_missing_feature(client):
response = client.post("/predict", json={"age": 35})
assert response.status_code == 422
В CI запускается pytest: merge блокируется, если новое поле запроса случайно сделало старый клиент несовместимым.
Что сделать после занятия
- [ ] Установите pytest как dev-зависимость и выполните
pytest. - [ ] Сделайте fixture с тестовым HTTP-клиентом.
- [ ] Напишите тесты на успешный и невалидный запрос к
/predict.