# Асинхронні тести { #async-tests }

Ви вже бачили, як тестувати ваші застосунки **FastAPI** за допомогою наданого `TestClient`. До цього часу ви бачили лише, як писати синхронні тести, без використання функцій `async`.

Можливість використовувати асинхронні функції у тестах може бути корисною, наприклад, коли ви асинхронно звертаєтеся до бази даних. Уявіть, що ви хочете протестувати надсилання запитів до вашого застосунку FastAPI, а потім перевірити, що ваш бекенд успішно записав коректні дані в базу даних, використовуючи асинхронну бібліотеку для бази даних.

Розгляньмо, як це реалізувати.

## pytest.mark.anyio { #pytest-mark-anyio }

Якщо ми хочемо викликати асинхронні функції у тестах, самі тестові функції мають бути асинхронними. AnyIO надає зручний плагін, який дозволяє вказати, що деякі тестові функції слід виконувати асинхронно.

## HTTPX { #httpx }

Навіть якщо ваш застосунок **FastAPI** використовує звичайні функції `def` замість `async def`, під капотом це все одно `async`-застосунок.

`TestClient` робить певну «магію» всередині, щоб викликати асинхронний застосунок FastAPI у ваших звичайних тестових функціях `def`, використовуючи стандартний pytest. Але ця «магія» більше не працює, коли ми використовуємо його всередині асинхронних функцій. Запускаючи тести асинхронно, ми більше не можемо використовувати `TestClient` у наших тестових функціях.

`TestClient` побудований на основі [HTTPX](https://www.python-httpx.org), і на щастя, ми можемо використовувати його безпосередньо для тестування API.

## Приклад { #example }

Для простого прикладу розгляньмо структуру файлів, подібну до описаної в [Більші застосунки](../tutorial/bigger-applications.md) та [Тестування](../tutorial/testing.md):

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

Файл `main.py` міститиме:

{* ../../docs_src/async_tests/app_a_py310/main.py *}

Файл `test_main.py` міститиме тести для `main.py`, тепер це може виглядати так:

{* ../../docs_src/async_tests/app_a_py310/test_main.py *}

## Запуск { #run-it }

Ви можете запустити тести як зазвичай:

<div class="termy">

```console
$ pytest

---> 100%
```

</div>

## Докладно { #in-detail }

Маркер `@pytest.mark.anyio` повідомляє pytest, що цю тестову функцію слід викликати асинхронно:

{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[7] *}

/// tip | Порада

Зауважте, що тестова функція тепер `async def` замість просто `def`, як це було раніше при використанні `TestClient`.

///

Далі ми можемо створити `AsyncClient` із застосунком і надсилати до нього асинхронні запити, використовуючи `await`.

{* ../../docs_src/async_tests/app_a_py310/test_main.py hl[9:12] *}

Це еквівалентно:

```Python
response = client.get('/')
```

...що ми раніше використовували для надсилання запитів за допомогою `TestClient`.

/// tip | Порада

Зауважте, що ми використовуємо async/await із новим `AsyncClient` - запит є асинхронним.

///

/// warning | Попередження

Якщо ваш застосунок залежить від подій тривалості життя, `AsyncClient` не ініціюватиме ці події. Щоб гарантувати їх ініціалізацію, використовуйте `LifespanManager` з [florimondmanca/asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan#usage).

///

## Інші асинхронні виклики функцій { #other-asynchronous-function-calls }

Оскільки тестова функція тепер асинхронна, ви також можете викликати (і `await`) інші `async`-функції окрім надсилання запитів до вашого застосунку FastAPI у тестах - так само, як ви робили б це будь-де у вашому коді.

/// tip | Порада

Якщо ви натрапили на `RuntimeError: Task attached to a different loop` під час інтеграції асинхронних викликів у ваші тести (наприклад, при використанні [MongoDB's MotorClient](https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop)), пам'ятайте створювати об'єкти, яким потрібен цикл подій, лише всередині асинхронних функцій, наприклад, у зворотному виклику `@app.on_event("startup")`.

///
