К этому моменту у вас есть всё: переменные и строки, словари и списки, циклы,
функции, assert и форма pytest. Осталось собрать из этого то, ради чего всё
затевалось, — автотесты API.
Тест шлёт запрос и проверяет четыре слоя, а качество самого теста доказывается только на сломанном сервисе.
Из чего состоит проект с тестами
tests/
conftest.py общие фикстуры: клиент, вход, тестовые данные
test_products.py тесты каталога
test_orders.py тесты заказов
requirements.txt список библиотек
Библиотек нужно ровно две: pytest — запускает тесты, requests — отправляет
запросы. Ставятся одной командой:
pip install pytest requests
В conftest.py создают клиента — объект, который помнит адрес сервиса и
заголовки:
import pytest
import requests
БАЗА = "https://shop.example.com/api"
@pytest.fixture
def client():
сессия = requests.Session()
сессия.base_url = БАЗА
return сессия
@pytest.fixture
def заголовки(client):
ответ = client.post(f"{БАЗА}/auth/login",
json={"email": "anna@example.com", "password": "secret123"})
assert ответ.status_code == 200, "не удалось войти — тесты дальше бессмысленны"
return {"Authorization": "Bearer " + ответ.json()["token"]}
В нашем тренажёре
фикстура client уже готова и ходит в игрушечный сервис — поэтому там код теста
такой же, а настраивать ничего не нужно.
Что проверять в ответе
Четыре слоя, от дешёвого к дорогому.
Код ответа. Самое первое: 200, 201, 404, 422. Проверяется всегда.
assert ответ.status_code == 201
Значимые поля тела. Не всё тело целиком: сравнение с полным словарём ломается от любого нового поля, которое обратную совместимость не нарушает.
заказ = ответ.json()
assert заказ["status"] == "NEW"
assert заказ["total"] == 4990
Заголовки, когда они часть контракта. Например, Location у созданного
объекта — по нему клиент идёт за результатом.
Последствия в системе. Самое ценное и чаще всего забытое: заказ создан — а остаток товара уменьшился? Ответ 201 об этом не говорит.
было = client.get(f"{БАЗА}/products/p-01").json()["inStock"]
client.post(f"{БАЗА}/orders", json={"productId": "p-01", "quantity": 2}, headers=заголовки)
стало = client.get(f"{БАЗА}/products/p-01").json()["inStock"]
assert стало == было - 2
Проверять отказы не менее важно
Тесты только на счастливый путь пропускают половину дефектов. На каждый сценарий полезно задать три вопроса: что при неверных данных, что без прав, что при конфликте.
живой пример
def test_заказ_без_токена(client):
ответ = client.post(f"{БАЗА}/orders", json={"productId": "p-01", "quantity": 1})
assert ответ.status_code == 401
def test_пустое_тело(client, заголовки):
ответ = client.post(f"{БАЗА}/orders", json={}, headers=заголовки)
assert ответ.status_code == 422
поля = [д["field"] for д in ответ.json()["error"]["details"]]
assert "productId" in поля
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
И отдельно — что отказ ничего не изменил: остаток на месте, заказ не создан. Отказ, который успел что-то поменять, — типичная находка на проверке прав.
Данные готовят запросом, а не кликами
Тест, которому нужен оплаченный заказ, не должен проходить оформление через интерфейс. Он создаёт нужное состояние запросами — быстро и повторяемо:
@pytest.fixture
def оплаченный_заказ(client, заголовки):
ответ = client.post(f"{БАЗА}/orders",
json={"productId": "p-01", "quantity": 1}, headers=заголовки)
return ответ.json()
И каждый тест готовит данные себе сам. Тесты, которые передают состояние друг другу, ломаются от смены порядка запуска и падают «по очереди» без причины.
Зелёный тест ничего не гарантирует
Главная мысль, ради которой стоило дочитать. Тест — такой же код, и в нём тоже бывают ошибки. Тест, который ничего не проверяет, зелёный всегда:
живой пример
def test_заказ(client, заголовки):
client.post(f"{БАЗА}/orders", json={"productId": "p-01", "quantity": 1}, headers=заголовки)
assert True # ← так бывает чаще, чем хотелось бы
Запустить
Запуск примеров доступен в платном доступе. Там этот же код выполняется прямо в статье: редактор, запуск и проверка рядом с абзацем. Неделя бесплатно →
Проверить свои тесты можно единственным честным способом: сломать систему и посмотреть, заметят ли они это. Отключите валидацию, уберите проверку токена, перестаньте списывать остаток — сколько тестов покраснеет? Те, что остались зелёными, не проверяют ничего.
Этот приём называется мутационным тестированием, и в тренажёре автотестов API он работает автоматически: ваши тесты прогоняются против девяти сломанных версий сервиса, и в отчёте видно, какие поломки они поймали, а какие прошли мимо. Зачёт ставится, только если тест хоть что-то ловит.
Куда двигаться дальше
Когда тесты на API пишутся уверенно, дальше обычно идут:
- запуск в конвейере сборки — чтобы тесты гонялись на каждое изменение, а не руками;
- отчёты и разбор нестабильных тестов;
- тесты интерфейса — они дороже и капризнее, поэтому их берут после API.
Но порядок именно такой: сначала уверенные тесты API, потом всё остальное.
Что решать
Банк «Автотесты API» — десять задач ровно по этой статье: коды ответов и поля, постраничная выдача, вход и токен, валидация, конфликты, создание заказа с последствиями, чужие данные и повторный запрос.
Коротко
- Проекту с тестами нужны две библиотеки:
pytestиrequests; общие фикстуры живут вconftest.py. - Проверяют четыре слоя: код ответа, значимые поля, заголовки контракта и последствия в системе.
- Отказы проверяют наравне со счастливым путём — и отдельно то, что отказ ничего не изменил.
- Данные готовят запросами, каждый тест — себе сам; передача состояния между тестами делает их нестабильными.
- Зелёный тест ничего не гарантирует, пока не показал, что краснеет на сломанной системе.
Что почитать дальше
- pytest: первый настоящий тест — если нужно освежить форму.
- API в Postman — те же запросы руками, когда нужно быстро посмотреть.