Spec-Zone.ru › FastAPI

Асинхронные тесты

Вы уже видели, как тестировать ваши приложения FastAPI, используя предоставленные TestClient. До сих пор вы видели только как писать синхронные тесты, без использования async функций.

Возможность использовать асинхронные функции в тестах может быть полезна, например, при асинхронном запросе к вашей базе данных. Представьте, что вы хотите протестировать отправку запросов вашему приложению FastAPI и затем проверить, что ваш бэкэнд успешно записал правильные данные в базу данных, используя асинхронную библиотеку базы данных.

Давайте посмотрим, как это можно сделать.

pytest.mark.anyio

Если мы хотим вызывать асинхронные функции в наших тестах, наши тестовые функции должны быть асинхронными. AnyIO предоставляет удобный плагин для этого, позволяющий указать, что некоторые тестовые функции должны вызываться асинхронно.

HTTPX

Даже если ваше приложение FastAPI использует обычные def функции вместо async def, это по-прежнему приложение async.

TestClient выполняет некоторую магию внутри, чтобы вызывать асинхронное приложение FastAPI в ваших обычных def тестовых функциях, используя стандартный pytest. Но эта магия больше не работает, когда мы используем её внутри асинхронных функций. Запуская наши тесты асинхронно, мы больше не можем использовать TestClient внутри наших тестовых функций.

TestClient основан на HTTPX, и, к счастью, мы можем использовать его напрямую для тестирования API.

Пример

Для простого примера рассмотрим структуру файлов, аналогичную той, что описана в Больших приложениях и Тестировании:

.
├── app
│   ├── __init__.py
│   ├── main.py
│   └── test_main.py

В файле main.py будет:

from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Tomato"}

В файле test_main.py будут тесты для main.py, они могут выглядеть так:

import pytest
from httpx import ASGITransport, AsyncClient

from .main import app


@pytest.mark.anyio
async def test_root():
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as ac:
        response = await ac.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Tomato"}

Запуск

Вы можете запустить тесты как обычно, используя:

$ pytest

---> 100%

Подробно

Маркер @pytest.mark.anyio сообщает pytest, что эта тестовая функция должна вызываться асинхронно:

import pytest
from httpx import ASGITransport, AsyncClient

from .main import app


@pytest.mark.anyio
async def test_root():
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as ac:
        response = await ac.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Tomato"}

Подсказка

Обратите внимание, что тестовая функция теперь async def вместо просто def, как раньше, когда использовался TestClient.

Затем мы можем создать AsyncClient с приложением и отправить асинхронные запросы к нему, используя await.

import pytest
from httpx import ASGITransport, AsyncClient

from .main import app


@pytest.mark.anyio
async def test_root():
    async with AsyncClient(
        transport=ASGITransport(app=app), base_url="http://test"
    ) as ac:
        response = await ac.get("/")
    assert response.status_code == 200
    assert response.json() == {"message": "Tomato"}

Это эквивалентно:

response = client.get('/')

…что мы использовали для отправки запросов с помощью TestClient.

Подсказка

Обратите внимание, что мы используем async/await с новым AsyncClient — запрос асинхронный.

Предупреждение

Если ваше приложение полагается на события жизненного цикла, AsyncClient не будет вызывать эти события. Чтобы гарантировать их срабатывание, используйте LifespanManager из florimondmanca/asgi-lifespan.

Другие вызовы асинхронных функций

Поскольку функция тестирования теперь асинхронна, вы можете также вызывать (и await) другие async функции, помимо отправки запросов вашему приложению FastAPI в ваших тестах, точно так же, как вы бы их вызывали в любом другом месте кода.

Подсказка

Если при интеграции асинхронных вызовов функций в ваших тестах (например, при использовании MongoDB's MotorClient) вы столкнулись с ошибкой, помните, что объекты, нуждающиеся в цикле событий, следует создавать только внутри асинхронных функций, например, в '@app.on_event("startup") обработчике.

Была ли эта страница полезной?
Спасибо за ваш отзыв!
Спасибо за ваш отзыв!

© 2018 Sebastián Ramírez
Licensed under the MIT License.
https://fastapi.tiangolo.com/advanced/async-tests/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API