REST API тестирование на Python: pytest + requests за час
Если ты ручной тестировщик и хочешь начать автоматизировать — REST API через Python — самый низкий порог входа. Никакого UI, никаких флэйков, никакого Selenium-сетапа. Только HTTP, JSON и assert’ы. Гайд за час.
Установка
pip install pytest requests
Готово. Никаких драйверов, никаких браузеров.
Первый тест
import requests
def test_get_user():
response = requests.get('https://api.example.com/users/1')
assert response.status_code == 200
user = response.json()
assert user['id'] == 1
assert 'email' in user
Запуск: pytest test_users.py -v.
Структура реальных тестов
import pytest
import requests
BASE_URL = 'https://api.example.com'
@pytest.fixture
def auth_headers():
response = requests.post(f'{BASE_URL}/auth/login', json={
'email': 'test@example.com',
'password': 'pass123'
})
token = response.json()['token']
return {'Authorization': f'Bearer {token}'}
def test_create_user(auth_headers):
response = requests.post(
f'{BASE_URL}/users',
headers=auth_headers,
json={'name': 'New User', 'email': 'new@example.com'}
)
assert response.status_code == 201
assert response.json()['name'] == 'New User'
def test_user_not_found(auth_headers):
response = requests.get(f'{BASE_URL}/users/999999', headers=auth_headers)
assert response.status_code == 404
Fixture auth_headers логинится один раз и передаётся в каждый тест — DRY-принцип в action.
Что тестировать
— Status codes: 200, 201, 400, 401, 403, 404, 500 — каждый соответствует ожиданию.
— JSON Schema: структура ответа стабильна. Используй jsonschema для валидации.
— Headers: Content-Type, Cache-Control, security headers (X-Frame-Options, etc.).
— Идемпотентность: GET, PUT, DELETE — повторный вызов с теми же параметрами даёт тот же результат.
— Boundary: пустой body, максимальный размер, спецсимволы, unicode, очень длинные строки.
— Авторизация: без токена → 401. С чужим токеном → 403.
Параметризация
Один тест — много инпутов:
@pytest.mark.parametrize('email,expected_status', [
('valid@example.com', 200),
('invalid-email', 400),
('', 400),
('a' * 1000 + '@example.com', 413), # too long
("user'; DROP TABLE--@example.com", 400), # SQL injection attempt
])
def test_login_email_validation(email, expected_status):
response = requests.post(f'{BASE_URL}/auth/login', json={'email': email, 'password': 'x'})
assert response.status_code == expected_status
5 тестов в одной функции.
Контракт-тесты
API-команда обновила endpoint? Сравнить ответ с зафиксированным контрактом:
import jsonschema
USER_SCHEMA = {
'type': 'object',
'required': ['id', 'email', 'created_at'],
'properties': {
'id': {'type': 'integer'},
'email': {'type': 'string', 'format': 'email'},
'created_at': {'type': 'string', 'format': 'date-time'},
}
}
def test_user_contract():
response = requests.get(f'{BASE_URL}/users/1')
jsonschema.validate(response.json(), USER_SCHEMA)
Бэкенд убрал created_at или поменял тип id → тест красный.
Полезные тулзы вокруг
— httpx (вместо requests) — поддерживает async, отлично для нагрузочных тестов.
— VCR.py — записывает HTTP-ответы один раз, потом playback’ит. Тесты не зависят от внешнего сервера.
— Allure — красивые HTML-отчёты.
— Postman — для exploration, дальше переноси в pytest.
С чего начать
✅ Возьми один endpoint из своего продукта, напиши 3 теста — positive, negative, boundary.
✅ Заведи tests/api/ директорию в репо разработки, прикрути pytest в CI.
✅ Подключи pytest --cov для отчёта о coverage.
Через неделю у тебя будет 30 API-тестов, гоняющихся на каждый PR. Через месяц — full smoke suite. Это самый быстрый путь от мануального QA к автоматизатору.
Подробнее: requests docs, pytest docs.