# Тестирование Благодаря Starlette, тестировать приложения **FastAPI** легко и приятно. Тестирование основано на библиотеке HTTPX, которая в свою очередь основана на библиотеке Requests, так что все действия знакомы и интуитивно понятны. Используя эти инструменты, Вы можете напрямую задействовать pytest с **FastAPI**. ## Использование класса `TestClient` /// info | Информация Для использования класса `TestClient` необходимо установить библиотеку `httpx`. Например, так: `pip install httpx`. /// Импортируйте `TestClient`. Создайте объект `TestClient`, передав ему в качестве параметра Ваше приложение **FastAPI**. Создайте функцию, название которой должно начинаться с `test_` (это стандарт из соглашений `pytest`). Используйте объект `TestClient` так же, как Вы используете `httpx`. Напишите простое утверждение с `assert` дабы проверить истинность Python-выражения (это тоже стандарт `pytest`). {* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *} /// tip | Подсказка Обратите внимание, что тестирующая функция является обычной `def`, а не асинхронной `async def`. И вызов клиента также осуществляется без `await`. Это позволяет вам использовать `pytest` без лишних усложнений. /// /// note | Технические детали Также можно написать `from starlette.testclient import TestClient`. **FastAPI** предоставляет тот же самый `starlette.testclient` как `fastapi.testclient`. Это всего лишь небольшое удобство для Вас, как разработчика. /// /// tip | Подсказка Если для тестирования Вам, помимо запросов к приложению FastAPI, необходимо вызывать асинхронные функции (например, для подключения к базе данных с помощью асинхронного драйвера), то ознакомьтесь со страницей [Асинхронное тестирование](../advanced/async-tests.md){.internal-link target=_blank} в расширенном руководстве. /// ## Разделение тестов и приложения В реальном приложении Вы, вероятно, разместите тесты в отдельном файле. Кроме того, Ваше приложение **FastAPI** может состоять из нескольких файлов, модулей и т.п. ### Файл приложения **FastAPI** Допустим, структура файлов Вашего приложения похожа на ту, что описана на странице [Более крупные приложения](bigger-applications.md){.internal-link target=_blank}: ``` . ├── app │   ├── __init__.py │   └── main.py ``` Здесь файл `main.py` является "точкой входа" в Ваше приложение и содержит инициализацию Вашего приложения **FastAPI**: {* ../../docs_src/app_testing/main.py *} ### Файл тестов Также у Вас может быть файл `test_main.py` содержащий тесты. Можно разместить тестовый файл и файл приложения в одной директории (в директориях для Python-кода желательно размещать и файл `__init__.py`): ``` hl_lines="5" . ├── app │   ├── __init__.py │   ├── main.py │   └── test_main.py ``` Так как оба файла находятся в одной директории, для импорта объекта приложения из файла `main` в файл `test_main` Вы можете использовать относительный импорт: {* ../../docs_src/app_testing/test_main.py hl[3] *} ...и писать дальше тесты, как и раньше. ## Тестирование: расширенный пример Теперь давайте расширим наш пример и добавим деталей, чтоб посмотреть, как тестировать различные части приложения. ### Расширенный файл приложения **FastAPI** Мы продолжим работу с той же файловой структурой, что и ранее: ``` . ├── app │   ├── __init__.py │   ├── main.py │   └── test_main.py ``` Предположим, что в файле `main.py` с приложением **FastAPI** есть несколько **операций пути**. В нём описана операция `GET`, которая может вернуть ошибку. Ещё есть операция `POST` и она тоже может вернуть ошибку. Обе *операции пути* требуют наличия в запросе заголовка `X-Token`. //// tab | Python 3.10+ ```Python {!> ../../docs_src/app_testing/app_b_an_py310/main.py!} ``` //// //// tab | Python 3.9+ ```Python {!> ../../docs_src/app_testing/app_b_an_py39/main.py!} ``` //// //// tab | Python 3.8+ ```Python {!> ../../docs_src/app_testing/app_b_an/main.py!} ``` //// //// tab | Python 3.10+ без Annotated /// tip | Подсказка По возможности используйте версию с `Annotated`. /// ```Python {!> ../../docs_src/app_testing/app_b_py310/main.py!} ``` //// //// tab | Python 3.8+ без Annotated /// tip | Подсказка По возможности используйте версию с `Annotated`. /// ```Python {!> ../../docs_src/app_testing/app_b/main.py!} ``` //// ### Расширенный файл тестов Теперь обновим файл `test_main.py`, добавив в него тестов: {* ../../docs_src/app_testing/app_b/test_main.py *} Если Вы не знаете, как передать информацию в запросе, можете воспользоваться поисковиком (погуглить) и задать вопрос: "Как передать информацию в запросе с помощью `httpx`", можно даже спросить: "Как передать информацию в запросе с помощью `requests`", поскольку дизайн HTTPX основан на дизайне Requests. Затем Вы просто применяете найденные ответы в тестах. Например: * Передаёте *path*-параметры или *query*-параметры, вписав их непосредственно в строку URL. * Передаёте JSON в теле запроса, передав Python-объект (например: `dict`) через именованный параметр `json`. * Если же Вам необходимо отправить *форму с данными* вместо JSON, то используйте параметр `data` вместо `json`. * Для передачи *заголовков*, передайте объект `dict` через параметр `headers`. * Для передачи *cookies* также передайте `dict`, но через параметр `cookies`. Для получения дополнительной информации о передаче данных на бэкенд с помощью `httpx` или `TestClient` ознакомьтесь с документацией HTTPX. /// info | Информация Обратите внимание, что `TestClient` принимает данные, которые можно конвертировать в JSON, но не модели Pydantic. Если в Ваших тестах есть модели Pydantic и Вы хотите отправить их в тестируемое приложение, то можете использовать функцию `jsonable_encoder`, описанную на странице [Кодировщик совместимый с JSON](encoder.md){.internal-link target=_blank}. /// ## Запуск тестов Далее Вам нужно установить `pytest`:
```console $ pip install pytest ---> 100% ```
Он автоматически найдёт все файлы и тесты, выполнит их и предоставит Вам отчёт о результатах тестирования. Запустите тесты командой `pytest` и увидите результат:
```console $ pytest ================ test session starts ================ platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1 rootdir: /home/user/code/superawesome-cli/app plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1 collected 6 items ---> 100% test_main.py ...... [100%] ================= 1 passed in 0.03s ================= ```