Клиент вернул ответ, но отправил неправильный запрос: тестируем MockTransport
Код интеграции получает mock-ответ от подменённого client.post и тест проходит. После изменения сериализации запрос всё ещё отправляется, но поле называется иначе, а обязательный заголовок отсутствует. Тест проверял только то, что функция вернула нужный результат из заранее приготовленного объекта. Сборка настоящего HTTP-запроса вообще не участвовала.
MockTransport позволяет оставить реальный HTTPX Client и заменить нижнюю границу отправки. Обработчик получает httpx.Request после сборки URL, заголовков и тела. Он может проверить контракт, затем вернуть Response. Сеть не используется, но преобразование прикладных аргументов в HTTP-запрос выполняет тот же клиентский код, что в приложении.
Настоящая сборка запроса
В примере функция создаёт задачу во внешнем API. Base URL принадлежит клиенту, путь и JSON — функции интеграции. Обработчик проверяет метод, путь, query-параметр, заголовок и разобранное тело. JSON сравнивается по данным, поэтому порядок ключей и пробелы сериализатора не становятся ненужной частью контракта.
import json
import httpx
def create_task(client: httpx.Client, title: str) -> int:
response = client.post(
"/tasks", params={"notify": "true"},
headers={"X-Request-Id": "req-1"}, json={"title": title},
)
response.raise_for_status()
return response.json()["id"]
def handler(request: httpx.Request) -> httpx.Response:
assert request.method == "POST"
assert request.url.path == "/tasks"
assert request.url.params["notify"] == "true"
assert request.headers["X-Request-Id"] == "req-1"
assert json.loads(request.content) == {"title": "report"}
return httpx.Response(201, json={"id": 42})
with httpx.Client(
base_url="https://api.invalid", transport=httpx.MockTransport(handler)
) as client:
result = create_task(client, "report")
assert result == 42
print(result)
Статусный отказ и ошибка сети
Установите пакет командой python -m pip install httpx, сохраните файл и запустите Python 3.12. Он напечатает 42. Пример проверен с HTTPX 0.28.1. Переименуйте поле title в text или уберите X-Request-Id: проверка упадёт внутри транспорта до возврата успешного ответа. Это полезный негативный опыт, показывающий, что запрос действительно проверяется.
Добавьте сценарий отказа внешнего сервиса: обработчик возвращает 503, а функция должна поднять HTTPStatusError или перевести его в согласованную ошибку интеграции. Отдельно смоделируйте сетевую ошибку через raise httpx.ConnectError(..., request=request). Статусный отказ и невозможность получить ответ — разные ветви клиента, их не стоит объединять одним искусственным Response.
Если интеграция делает несколько вызовов, храните список полученных запросов или состояние внутри обработчика. Проверяйте только требуемый порядок и ключевые поля. Не копируйте в ожидания все автоматически добавленные заголовки HTTPX: обновление библиотеки не должно ломать тест из-за детали, которую внешний сервис не использует.
MockTransport не проверяет DNS, TLS, прокси и поведение настоящего сервера. Для них оставьте небольшой интеграционный набор с контролируемой средой. Здесь цель конкретнее: убедиться, что функция правильно использует HTTP-протокол на своей границе. Такой тест заметно полезнее заглушки, которая всегда возвращает успех независимо от отправленного метода и содержимого.
Следующий шаг — перенести этот приём из отдельного файла в поддерживаемое приложение. В курсе «Python для продвинутых: ООП, типизация и тестирование» структура пакетов, объектная модель, mypy и pytest соединяются в типизированном трекере задач с хранением данных и логированием. Можно отработать связь между интерфейсом, реализацией и регрессионным тестом, а затем применить её к своему проекту.
Посмотреть программу и практику курса.
Документация: официальный источник по теме.
Другие задачи по теме: База, HTTP-клиенты и тесты API.
Продолжить на практике
- Python для продвинутых: ООП, типизация и тестирование ↗
Практический курс-мост от базового Python к разработке поддерживаемых приложений. Вы разберётесь со структурой пакетов, объектной моделью, современной типизацией и автоматическими тестами, а затем соедините эти инструменты в небольшом приложении с хранением данных и логированием.
