Browse Source
Co-authored-by: Valentyn Druzhynin <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Rostyslav <[email protected]> Co-authored-by: Sofie Van Landeghem <[email protected]>pull/13432/head
committed by
GitHub
1 changed files with 240 additions and 0 deletions
@ -0,0 +1,240 @@ |
|||||
|
# Тестування |
||||
|
|
||||
|
Тестування **FastAPI** додатків є простим та ефективним завдяки бібліотеці <a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a>, яка базується на <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX</a>. |
||||
|
Оскільки HTTPX розроблений на основі Requests, його API є інтуїтивно зрозумілим для тих, хто вже знайомий з Requests. |
||||
|
|
||||
|
З його допомогою Ви можете використовувати <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a> безпосередньо з **FastAPI**. |
||||
|
|
||||
|
## Використання `TestClient` |
||||
|
|
||||
|
/// info | Інформація |
||||
|
|
||||
|
Щоб використовувати `TestClient`, спочатку встановіть <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a>. |
||||
|
|
||||
|
Переконайтеся, що Ви створили [віртуальне середовище](../virtual-environments.md){.internal-link target=_blank}, активували його, а потім встановили саму бібліотеку, наприклад: |
||||
|
|
||||
|
```console |
||||
|
$ 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` для зручності розробників, але він безпосередньо походить із Starlette. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
/// tip | Порада |
||||
|
|
||||
|
Якщо Вам потрібно викликати `async`-функції у ваших тестах, окрім відправлення запитів до 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 |
||||
|
``` |
||||
|
|
||||
|
Оскільки цей файл знаходиться в тому ж пакеті, Ви можете використовувати відносний імпорт, щоб імпортувати об'єкт `app` із модуля `main` (`main.py`): |
||||
|
|
||||
|
{* ../../docs_src/app_testing/test_main.py hl[3] *} |
||||
|
|
||||
|
|
||||
|
...і написати код для тестів так само як і раніше. |
||||
|
|
||||
|
## Тестування: розширений приклад |
||||
|
|
||||
|
Тепер розширимо цей приклад і додамо більше деталей, щоб побачити, як тестувати різні частини. |
||||
|
|
||||
|
### Розширений файл застосунку **FastAPI** |
||||
|
|
||||
|
Залишимо ту саму структуру файлів: |
||||
|
|
||||
|
``` |
||||
|
. |
||||
|
├── app |
||||
|
│ ├── __init__.py |
||||
|
│ ├── main.py |
||||
|
│ └── test_main.py |
||||
|
``` |
||||
|
|
||||
|
Припустимо, що тепер файл `main.py` із Вашим **FastAPI**-застосунком містить додаткові операції шляху (**path operations**). |
||||
|
|
||||
|
Він має `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+ non-Annotated |
||||
|
|
||||
|
/// tip | Порада |
||||
|
|
||||
|
Бажано використовувати версію з `Annotated`, якщо це можливо |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../docs_src/app_testing/app_b_py310/main.py!} |
||||
|
``` |
||||
|
|
||||
|
//// |
||||
|
|
||||
|
//// tab | Python 3.8+ non-Annotated |
||||
|
|
||||
|
/// tip | Порада |
||||
|
|
||||
|
Бажано використовувати версію з `Annotated`, якщо це можливо |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
```Python |
||||
|
{!> ../../docs_src/app_testing/app_b/main.py!} |
||||
|
``` |
||||
|
|
||||
|
//// |
||||
|
|
||||
|
### Розширений тестовий файл |
||||
|
|
||||
|
Потім Ви можете оновити `test_main.py`, додавши розширені тести: |
||||
|
|
||||
|
{* ../../docs_src/app_testing/app_b/test_main.py *} |
||||
|
|
||||
|
Коли Вам потрібно передати клієнту інформацію в запиті, але Ви не знаєте, як це зробити, Ви можете пошукати (наприклад, у Google) спосіб реалізації в `httpx`, або навіть у `requests`, оскільки HTTPX розроблений на основі дизайну Requests. |
||||
|
|
||||
|
Далі Ви просто повторюєте ці ж дії у ваших тестах. |
||||
|
|
||||
|
Наприклад: |
||||
|
|
||||
|
* Щоб передати *path* або *query* параметр, додайте його безпосередньо до URL. |
||||
|
* Щоб передати тіло JSON, передайте Python-об'єкт (наприклад, `dict`) у параметр `json`. |
||||
|
* Якщо потрібно надіслати *Form Data* замість JSON, використовуйте параметр `data`. |
||||
|
* Щоб передати заголовки *headers*, використовуйте `dict` у параметрі `headers`. |
||||
|
* Для *cookies* використовуйте `dict` у параметрі `cookies`. |
||||
|
|
||||
|
Докладніше про передачу даних у бекенд (за допомогою `httpx` або `TestClient`) можна знайти в <a href="https://www.python-httpx.org" class="external-link" target="_blank">документації HTTPX</a>. |
||||
|
|
||||
|
/// info | Інформація |
||||
|
|
||||
|
Зверніть увагу, що `TestClient` отримує дані, які можна конвертувати в JSON, а не Pydantic-моделі. |
||||
|
Якщо у Вас є Pydantic-модель у тесті, і Ви хочете передати її дані в додаток під час тестування, Ви можете використати `jsonable_encoder`, описаний у розділі [JSON Compatible Encoder](encoder.md){.internal-link target=_blank}. |
||||
|
|
||||
|
/// |
||||
|
|
||||
|
## Запуск тестів |
||||
|
|
||||
|
Після цього вам потрібно встановити `pytest`. |
||||
|
|
||||
|
Переконайтеся, що Ви створили [віртуальне середовище]{.internal-link target=_blank}, активували його і встановили необхідні пакети, наприклад: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```console |
||||
|
$ pip install pytest |
||||
|
|
||||
|
---> 100% |
||||
|
``` |
||||
|
|
||||
|
</div> |
||||
|
|
||||
|
`pytest` автоматично знайде файли з тестами, виконає їх і надасть вам результати. |
||||
|
|
||||
|
Запустіть тести за допомогою: |
||||
|
|
||||
|
<div class="termy"> |
||||
|
|
||||
|
```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 <span style="color: green; white-space: pre;">...... [100%]</span> |
||||
|
|
||||
|
<span style="color: green;">================= 1 passed in 0.03s =================</span> |
||||
|
``` |
||||
|
|
||||
|
</div> |
Loading…
Reference in new issue