committed by
GitHub
1 changed files with 243 additions and 0 deletions
@ -0,0 +1,243 @@ |
|||
# ํ
์คํ
|
|||
|
|||
<a href="https://www.starlette.io/testclient/" class="external-link" target="_blank">Starlette</a> ๋๋ถ์ **FastAPI** ๋ฅผ ํ
์คํธํ๋ ์ผ์ ์ฝ๊ณ ์ฆ๊ฑฐ์ด ์ผ์ด ๋์์ต๋๋ค. |
|||
|
|||
Starlette๋ <a href="https://www.python-httpx.org\" class="external-link" target="_blank">HTTPX</a>๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ฉฐ, ์ด๋ Requests๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ค๊ณ๋์๊ธฐ ๋๋ฌธ์ ๋งค์ฐ ์น์ํ๊ณ ์ง๊ด์ ์
๋๋ค. |
|||
|
|||
์ด๋ฅผ ์ฌ์ฉํ๋ฉด FastAPI์์ <a href="https://docs.pytest.org/" class="external-link" target="_blank">pytest</a>๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค. |
|||
|
|||
## `TestClient` ์ฌ์ฉํ๊ธฐ |
|||
|
|||
/// info | ์ ๋ณด |
|||
|
|||
`TestClient` ์ฌ์ฉํ๋ ค๋ฉด, ์ฐ์ <a href="https://www.python-httpx.org" class="external-link" target="_blank">`httpx`</a> ๋ฅผ ์ค์นํด์ผ ํฉ๋๋ค. |
|||
|
|||
[virtual environment](../virtual-environments.md){.internal-link target=_blank} ๋ฅผ ๋ง๋ค๊ณ , ํ์ฑํ ์ํจ ๋ค์ ์ค์นํ์ธ์. ์์: |
|||
|
|||
```console |
|||
$ pip install httpx |
|||
``` |
|||
|
|||
/// |
|||
|
|||
`TestClient` ๋ฅผ ์ํฌํธํ์ธ์. |
|||
|
|||
**FastAPI** ์ดํ๋ฆฌ์ผ์ด์
์ ์ ๋ฌํ์ฌ `TestClient` ๋ฅผ ๋ง๋์ธ์. |
|||
|
|||
์ด๋ฆ์ด `test_` ๋ก ์์ํ๋ ํจ์๋ฅผ ๋ง๋์ธ์(`pytest` ์ ํ์ค์ ์ธ ๊ด๋ก์
๋๋ค). |
|||
|
|||
`httpx` ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก `TestClient` ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ธ์. |
|||
|
|||
ํ์ค์ ์ธ ํ์ด์ฌ ๋ฌธ๋ฒ์ ์ด์ฉํ์ฌ ํ์ธ์ด ํ์ํ ๊ณณ์ ๊ฐ๋จํ `assert` ๋ฌธ์ฅ์ ์์ฑํ์ธ์(์ญ์ ํ์ค์ ์ธ `pytest` ๊ด๋ก์
๋๋ค). |
|||
|
|||
{* ../../docs_src/app_testing/tutorial001.py hl[2,12,15:18] *} |
|||
|
|||
/// tip | ํ |
|||
|
|||
ํ
์คํธ๋ฅผ ์ํ ํจ์๋ `async def` ๊ฐ ์๋๋ผ `def` ๋ก ์์ฑ๋จ์ ์ฃผ์ํ์ธ์. |
|||
|
|||
๊ทธ๋ฆฌ๊ณ ํด๋ผ์ด์ธํธ์ ๋ํ ํธ์ถ๋ `await` ๋ฅผ ์ฌ์ฉํ์ง ์๋ ์ผ๋ฐ ํธ์ถ์
๋๋ค. |
|||
|
|||
์ด๋ ๊ฒ ํ์ฌ ๋ณต์กํ ๊ณผ์ ์์ด `pytest` ๋ฅผ ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. |
|||
|
|||
/// |
|||
|
|||
/// note | ๊ธฐ์ ์ธ๋ถ์ฌํญ |
|||
|
|||
`from starlette.testclient import TestClient` ์ญ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. |
|||
|
|||
**FastAPI** ๋ ๊ฐ๋ฐ์์ ํธ์๋ฅผ ์ํด `starlette.testclient` ๋ฅผ `fastapi.testclient` ๋ก๋ ์ ๊ณตํ ๋ฟ์
๋๋ค. ์ด๋ ๋จ์ง `Starlette` ์์ ์ง์ ๊ฐ์ ธ์ค๋์ง์ ์ฐจ์ด์ผ ๋ฟ์
๋๋ค. |
|||
|
|||
/// |
|||
|
|||
/// tip | ํ |
|||
|
|||
FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ ์์ฒญ์ ๋ณด๋ด๋ ๊ฒ ์ธ์๋ ํ
์คํธ์์ `async` ํจ์๋ฅผ ํธ์ถํ๊ณ ์ถ๋ค๋ฉด (์: ๋น๋๊ธฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํจ์), ์ฌํ ํํ ๋ฆฌ์ผ์ [Async Tests](../advanced/async-tests.md){.internal-link target=_blank} ๋ฅผ ์ฐธ์กฐํ์ธ์. |
|||
|
|||
/// |
|||
|
|||
## ํ
์คํธ ๋ถ๋ฆฌํ๊ธฐ |
|||
|
|||
์ค์ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ํ
์คํธ๋ฅผ ๋ณ๋์ ํ์ผ๋ก ๋๋๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. |
|||
|
|||
|
|||
๊ทธ๋ฆฌ๊ณ **FastAPI** ์ ํ๋ฆฌ์ผ์ด์
๋ ์ฌ๋ฌ ํ์ผ์ด๋ ๋ชจ๋ ๋ฑ์ผ๋ก ๊ตฌ์ฑ๋ ์ ์์ต๋๋ค. |
|||
|
|||
### **FastAPI** app ํ์ผ |
|||
|
|||
[Bigger Applications](bigger-applications.md){.internal-link target=_blank} ์ ๋ฌ์ฌ๋ ํ์ผ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฒ์ผ๋ก ๊ฐ์ ํด๋ด
์๋ค. |
|||
|
|||
``` |
|||
. |
|||
โโโ app |
|||
โย ย โโโ __init__.py |
|||
โย ย โโโ main.py |
|||
``` |
|||
|
|||
`main.py` ํ์ผ ์์ **FastAPI** app ์ ๋ง๋ค์์ต๋๋ค: |
|||
|
|||
{* ../../docs_src/app_testing/main.py *} |
|||
|
|||
### ํ
์คํธ ํ์ผ |
|||
|
|||
ํ
์คํธ๋ฅผ ์ํด `test_main.py` ๋ผ๋ ํ์ผ์ ์์ฑํ ์ ์์ต๋๋ค. ์ด ํ์ผ์ ๋์ผํ Python ํจํค์ง(์ฆ, `__init__.py` ํ์ผ์ด ์๋ ๋์ผํ ๋๋ ํฐ๋ฆฌ)์ ์์นํ ์ ์์ต๋๋ค. |
|||
|
|||
``` hl_lines="5" |
|||
. |
|||
โโโ app |
|||
โย ย โโโ __init__.py |
|||
โย ย โโโ main.py |
|||
โย ย โโโ test_main.py |
|||
``` |
|||
|
|||
ํ์ผ๋ค์ด ๋์ผํ ํจํค์ง์ ์์นํด ์์ผ๋ฏ๋ก, ์๋ ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํ์ฌ `main` ์์ `app` ๊ฐ์ฒด๋ฅผ ์ํฌํธ ํด์ฌ ์ ์์ต๋๋ค. |
|||
|
|||
{* ../../docs_src/app_testing/test_main.py hl[3] *} |
|||
|
|||
|
|||
...๊ทธ๋ฆฌ๊ณ ์ด์ ์ ์์ฑํ๋ ๊ฒ๊ณผ ๊ฐ์ ํ
์คํธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. |
|||
|
|||
## ํ
์คํธ: ํ์ฅ๋ ์์ |
|||
|
|||
์ด์ ์์ ์์๋ฅผ ํ์ฅํ๊ณ ๋ ๋ง์ ์ธ๋ถ ์ฌํญ์ ์ถ๊ฐํ์ฌ ๋ค์ํ ๋ถ๋ถ์ ์ด๋ป๊ฒ ํ
์คํธํ๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค. |
|||
|
|||
### ํ์ฅ๋ FastAPI ์ ํ๋ฆฌ์ผ์ด์
ํ์ผ |
|||
|
|||
์ด์ ๊ณผ ๊ฐ์ ํ์ผ ๊ตฌ์กฐ๋ฅผ ๊ณ์ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค. |
|||
|
|||
``` |
|||
. |
|||
โโโ app |
|||
โย ย โโโ __init__.py |
|||
โย ย โโโ main.py |
|||
โย ย โโโ test_main.py |
|||
``` |
|||
|
|||
์ด์ **FastAPI** ์ฑ์ด ์๋ `main.py` ํ์ผ์ ๋ช ๊ฐ์ง ๋ค๋ฅธ **๊ฒฝ๋ก ์์
** ์ด ์ถ๊ฐ๋ ๊ฒฝ์ฐ๋ฅผ ์๊ฐํด๋ด
์๋ค. |
|||
|
|||
๋จ์ผ ์ค๋ฅ๋ฅผ ๋ฐํํ ์ ์๋ `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 *} |
|||
|
|||
|
|||
ํด๋ผ์ด์ธํธ๊ฐ ์์ฒญ์ ์ ๋ณด๋ฅผ ์ ๋ฌํด์ผ ํ๋๋ฐ ๋ฐฉ๋ฒ์ ๋ชจ๋ฅด๊ฒ ๋ค๋ฉด, `httpx`์์ ํด๋น ์์
์ ์ํํ๋ ๋ฐฉ๋ฒ์ ๊ฒ์(Google)ํ๊ฑฐ๋, `requests`์์์ ๋ฐฉ๋ฒ์ ๊ฒ์ํด๋ณด์ธ์. HTTPX๋ Requests์ ๋์์ธ์ ๊ธฐ๋ฐ์ผ๋ก ์ค๊ณ๋์์ต๋๋ค. |
|||
|
|||
๊ทธ ํ, ํ
์คํธ์์๋ ๋์ผํ๊ฒ ์ ์ฉํ๋ฉด ๋ฉ๋๋ค. |
|||
|
|||
์์: |
|||
|
|||
* *๊ฒฝ๋ก* ํน์ *์ฟผ๋ฆฌ* ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํ๋ ค๋ฉด, URL ์์ฒด์ ์ถ๊ฐํ๋ค. |
|||
* JSON ๋ณธ๋ฌธ์ ์ ๋ฌํ๋ ค๋ฉด, ํ์ด์ฌ ๊ฐ์ฒด (์๋ฅผ๋ค๋ฉด `dict`) ๋ฅผ `json` ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ๋ค. |
|||
* JSON ๋์ *ํผ ๋ฐ์ดํฐ* ๋ฅผ ๋ณด๋ด์ผํ๋ค๋ฉด, `data` ํ๋ผ๋ฏธํฐ๋ฅผ ๋์ ์ ๋ฌํ๋ค. |
|||
* *ํค๋* ๋ฅผ ์ ๋ฌํ๋ ค๋ฉด, `headers` ํ๋ผ๋ฏธํฐ์ `dict` ๋ฅผ ์ ๋ฌํ๋ค. |
|||
* *์ฟ ํค* ๋ฅผ ์ ๋ฌํ๋ ค๋ฉด, `cookies` ํ๋ผ๋ฏธํฐ์ `dict` ๋ฅผ ์ ๋ฌํ๋ค. |
|||
|
|||
๋ฐฑ์๋๋ก ๋ฐ์ดํฐ๋ฅผ ์ด๋ป๊ฒ ๋ณด๋ด๋์ง ์ ๋ณด๋ฅผ ๋ ์ป์ผ๋ ค๋ฉด (`httpx` ํน์ `TestClient` ๋ฅผ ์ด์ฉํด์) <a href="https://www.python-httpx.org" class="external-link" target="_blank">HTTPX documentation</a> ๋ฅผ ํ์ธํ์ธ์. |
|||
|
|||
/// info | ์ ๋ณด |
|||
|
|||
`TestClient` ๋ Pydantic ๋ชจ๋ธ์ด ์๋๋ผ JSON ์ผ๋ก ๋ณํ๋ ์ ์๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ต๋๋ค. |
|||
|
|||
๋ง์ฝ ํ
์คํธ์ค Pydantic ๋ชจ๋ธ์ ์ดํ๋ฆฌ์ผ์ด์
์ผ๋ก์ ๋ณด๋ด๊ณ ์ถ๋ค๋ฉด, [JSON ํธํ ๊ฐ๋ฅ ์ธ์ฝ๋](encoder.md){.internal-link target=_blank} ์ ์ค๋ช
๋์ด ์๋ `jsonable_encoder` ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. |
|||
|
|||
/// |
|||
|
|||
## ์คํํ๊ธฐ |
|||
|
|||
ํ
์คํธ ์ฝ๋๋ฅผ ์์ฑํ๊ณ , `pytest` ๋ฅผ ์ค์นํด์ผํฉ๋๋ค. |
|||
|
|||
[virtual environment](../virtual-environments.md){.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