Вы уже видели как тестировать FastAPI приложение, используя имеющийся класс TestClient. К этому моменту вы видели только как писать тесты в синхронном стиле без использования async функций.
Возможность использования асинхронных функций в ваших тестах может быть полезнa, когда, например, вы асинхронно обращаетесь к вашей базе данных. Представьте, что вы хотите отправить запросы в ваше FastAPI приложение, а затем при помощи асинхронной библиотеки для работы с базой данных удостовериться, что ваш бекэнд корректно записал данные в базу данных.
Если мы хотим вызывать асинхронные функции в наших тестах, то наши тестовые функции должны быть асинхронными. AnyIO предоставляет для этого отличный плагин, который позволяет нам указывать, какие тестовые функции должны вызываться асинхронно.
Даже если FastAPI приложение использует обычные функции def вместо async def, это все равно async приложение 'под капотом'.
Чтобы работать с асинхронным FastAPI приложением в ваших обычных тестовых функциях def, используя стандартный pytest, TestClient внутри себя делает некоторую магию. Но эта магия перестает работать, когда мы используем его внутри асинхронных функций. Запуская наши тесты асинхронно, мы больше не можем использовать TestClient внутри наших тестовых функций.
TestClient основан на HTTPX, и, к счастью, мы можем использовать его (HTTPX) напрямую для тестирования API.
...которое мы использовали для отправки наших запросов с TestClient.
Подсказка
Обратите внимание, что мы используем async/await с AsyncClient - запрос асинхронный.
Внимание
Если ваше приложение полагается на lifespan события, то AsyncClient не запустит эти события. Чтобы обеспечить их срабатывание используйте LifespanManager из florimondmanca/asgi-lifespan.
Теперь тестовая функция стала асинхронной, поэтому внутри нее вы можете вызывать также и другие async функции, не связанные с отправлением запросов в ваше FastAPI приложение. Как если бы вы вызывали их в любом другом месте вашего кода.
Подсказка
Если вы столкнулись с RuntimeError: Task attached to a different loop при вызове асинхронных функций в ваших тестах (например, при использовании MongoDB's MotorClient), то не забывайте инициализировать объекты, которым нужен цикл событий (event loop), только внутри асинхронных функций, например, в '@app.on_event("startup") callback.