Skip to content

Latest commit

 

History

History
212 lines (133 loc) · 10.2 KB

testing.md

File metadata and controls

212 lines (133 loc) · 10.2 KB

Тестирование

Благодаря 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!}

!!! tip "Подсказка" Обратите внимание, что тестирующая функция является обычной def, а не асинхронной async def.

И вызов клиента также осуществляется без `await`.

Это позволяет вам использовать `pytest` без лишних усложнений.

!!! note "Технические детали" Также можно написать from starlette.testclient import TestClient.

**FastAPI** предоставляет тот же самый `starlette.testclient` как `fastapi.testclient`. Это всего лишь небольшое удобство для вас, как разработчика.

!!! tip "Подсказка" Если для тестирования вам, помимо запросов к приложению FastAPI, необходимо вызывать асинхронные функции (например, для подключения к базе данных с помощью асинхронного драйвера), то ознакомьтесь со страницей Асинхронное тестирование{.internal-link target=_blank} в расширенном руководстве.

Разделение тестов и приложения

В реальном приложении вы, вероятно, разместите тесты в отдельном файле.

Кроме того, ваше приложение FastAPI может состоять из нескольких файлов, модулей и т.п.

Файл приложения FastAPI

Допустим, структура файлов вашего приложения похожа на ту, что описана на странице Более крупные приложения{.internal-link target=_blank}:

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

Здесь файл main.py является "точкой входа" в ваше приложение и содержит инициализацию вашего приложения FastAPI:

{!../../../docs_src/app_testing/main.py!}

Файл тестов

Также у вас может быть файл test_main.py содержащий тесты. Можно разместить тестовый файл и файл приложения в одной директории (в директориях для Python-кода желательно размещать и файл __init__.py):

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

Так как оба файла находятся в одной директории, для импорта объекта приложения из файла main в файл test_main вы можете использовать относительный импорт:

{!../../../docs_src/app_testing/test_main.py!}

...и писать дальше тесты, как и раньше.

Тестирование: расширенный пример

Теперь давайте расширим наш пример и добавим деталей, чтоб посмотреть, как тестировать различные части приложения.

Расширенный файл приложения FastAPI

Мы продолжим работу с той же файловой структурой, что и ранее:

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

Предположим, что в файле main.py с приложением FastAPI есть несколько операций пути.

В нём описана операция GET, которая может вернуть ошибку.

Ещё есть операция POST и она тоже может вернуть ошибку.

Обе операции пути требуют наличия в запросе заголовка X-Token.

=== "Python 3.10+"

```Python
{!> ../../../docs_src/app_testing/app_b_an_py310/main.py!}
```

=== "Python 3.9+"

```Python
{!> ../../../docs_src/app_testing/app_b_an_py39/main.py!}
```

=== "Python 3.6+"

```Python
{!> ../../../docs_src/app_testing/app_b_an/main.py!}
```

=== "Python 3.10+ без Annotated"

!!! tip "Подсказка"
    По возможности используйте версию с `Annotated`.

```Python
{!> ../../../docs_src/app_testing/app_b_py310/main.py!}
```

=== "Python 3.6+ без 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:

$ pip install pytest

---> 100%

Он автоматически найдёт все файлы и тесты, выполнит их и предоставит вам отчёт о результатах тестирования.

Запустите тесты командой pytest и увидите результат:

$ 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 <span style="color: green; white-space: pre;">......                            [100%]</span>

<span style="color: green;">================= 1 passed in 0.03s =================</span>