Короткий ответ
Нужно замокать HTTP-слой (через unittest.mock, библиотеки responses/respx/requests-mock или тестовый HTTP-сервер), чтобы тест проверял логику обработки ответа и ошибок, не выполняя реальных сетевых запросов, и отдельно — через contract-тесты или интеграционные тесты против тестового окружения внешнего сервиса — проверять, что реальный контракт не разошёлся с ожиданиями.
Как это работает подробнее
unit-тесты с моками подменяют HTTP-клиент или сам транспортный слой, задавая заранее подготовленные ответы (успех, разные коды ошибок, таймаут) для проверки, что код правильно обрабатывает каждый случай. Такие тесты быстрые и детерминированные, но не гарантируют, что реальный внешний API ведёт себя так, как в моке.
Пример кода
example.py
import responses
import requests
def get_exchange_rate(base: str) -> float:
resp = requests.get(f"https://fx.example.com/rate/{base}", timeout=3)
resp.raise_for_status()
return resp.json()["rate"]
@responses.activate
def test_get_exchange_rate_success():
responses.add(
responses.GET, "https://fx.example.com/rate/USD",
json={"rate": 91.5}, status=200,
)
assert get_exchange_rate("USD") == 91.5
@responses.activate
def test_get_exchange_rate_server_error():
responses.add(responses.GET, "https://fx.example.com/rate/USD", status=503)
with pytest.raises(requests.exceptions.HTTPError):
get_exchange_rate("USD")Что использовать на практике
- Рекомендация. стоит разделять уровни тестирования: быстрые unit-тесты с моками для покрытия всех веток обработки ошибок (таймаут, 4xx, 5xx, неожиданный формат ответа), и отдельные (реже запускаемые, например в CI по расписанию или вручную) контрактные/интеграционные тесты против реального или sandbox-окружения партнёра, чтобы вовремя заметить, если внешний API изменил формат ответа без предупреждения.
Подводные камни
- Риски. мокать на уровне, который слишком далёк от реального HTTP-взаимодействия (например, мокать саму бизнес-функцию, которая вызывает API, вместо мокания HTTP-уровня), из-за чего тест не проверяет реальную логику обработки ответа и ошибок; никогда не проверять реальный контракт внешнего API, полагаясь только на моки, из-за чего расхождение с реальным сервисом обнаруживается только в production.