[FastAPI] 12. Pytest / UnitTest๋ฅผ ์ด์ฉํ ํ ์คํธ ์ฝ๋ ์์ฑ
์ด๋ฒ ํฌ์คํธ์์๋ Pytest์ Unit Test๋ฅผ ์ด์ฉํ์ฌ FastAPI์์ ์์ฑํ API๋ฅผ ํ ์คํธํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
Pytest
Python ์ธ์ด์์ ์ง์ํ๋ ํ ์คํธ ํ๋ ์์ํฌ์๋ Pytest๊ฐ ์์ต๋๋ค. Pytest๋ Python์์ ๊ฐ๋จํ ์ฝ๋์ ๋ํด ์ฌํํ๊ณ ๊ฐ๊ฒฐํ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋๋ก ๋์์ฃผ๋ฉฐ ๋ค์ํ ํ๋ฌ๊ทธ์ธ๋ค์ ํ์ฉํ์ฌ ๊ท๋ชจ๊ฐ ํฌ๊ณ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํด์๋ ํ ์คํธ๋ฅผ ์ฝ๊ฒ ํ ์ ์๋๋ก ๋์์ฃผ๋ ํ๋ ์์ํฌ์ ๋๋ค.
์ฃผ์ ํน์ง์ผ๋ก ์๋์ ๊ฐ์ต๋๋ค.
- ํ ์คํธ ์ฝ๋ ์คํ์ ์คํจํ ๊ฒฝ์ฐ ์์ธํ ์ ๋ณด ํ์ ์ง์
- ํ ์คํธ ๋ชจ๋ ๋ฐ ๊ธฐ๋ฅ์ ์๋์ผ๋ก ๊ฒ์ํด์ฃผ๋ ๊ธฐ๋ฅ ์ง์
- Session๊ณผ ๊ฐ์ ์๋ช ์ด ๊ธด ๋ฆฌ์์ค๋ฅผ ๋งค๊ฐ๋ณ์ํ ํด์ฃผ๊ณ , ์ด๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋งค์ปค๋์ฆ ์ ๊ณต
- unittest, nose test์ ๊ฐ์ ๋ค๋ฅธ ํ ์คํธ ์ ํ๊ตฐ ํธํ
- Python 3.6, PyPy 3 ์ง์
ํนํ Pytest์์ ์ง์ํ๋ Fixture ๊ธฐ๋ฅ์ ํ ์คํธ ์ฝ๋ ์์ฑ์ ๋์ฑ ์ฉ์ดํ๊ฒ ํด์ค๋๋ค.
UnitTest
UnitTest๋ Python์์ ์ฌ์ฉํ ์ ์๋ ๋จ์ ํ ์คํธ ํ๋ ์์ํฌ๋ก Java์ ๋จ์ ํ ์คํธ ํ๋ ์์ํฌ์ธ JUnit์์ ์๊ฐ๋ฐ์ ํ ์คํธ ํ๋ ์์ํฌ์ ๋๋ค. ์ฃผ์ ๊ธฐ๋ฅ์ผ๋ก ํ ์คํธ ์๋ํ, ํ ์คํธ ์๋ช ์ฃผ๊ธฐ, ํ ์คํธ ํต๊ณ๋ฅผ ์ง์ํด์ค๋๋ค.
์ค์ ๋ก FastAPI์์ Layered Architecture๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ด๋ฌํ ์ํคํ ์ฒ๋ฅผ ์ ๋ง์ถฐ์ง ์ํ๋ก ์ฝ๋๊ฐ ์คํ๋๊ณ ์๋์ง๋ฅผ ํ ์คํธ ํด๋ณด๊ณ ์ถ์ ๋๊ฐ ์๊ธฐ๋๋ฐ, ์ด ๋ UnitTest๋ฅผ ์ฌ์ฉํ๋ฉด ๋งค์ฐ ์ ์ฉํ๊ฒ ํ์ฉํ ์ ์์ต๋๋ค.
ํนํ UnitTest๋ Pytest์ ๋ฌ๋ฆฌ ๊ฐ์ฒด ์งํฅ(Object-oriented) ๋ฐฉ์์ ์ง์ํ์ฌ JUnit๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ํ ์คํธ ์คํ์ ์กฐ์ ํ ์ ์๊ณ , ์ด๋ฅผ ๋ง์๋๋ก ์ฃผ๋ฌด๋ฅผ ์๋ ์์ต๋๋ค. UnitTest์์ ์ง์ํ๋ Test Runner๋ PyQt์ ๊ฐ์ GUI ์ ํ๋ฆฌ์ผ์ด์ ํ ์คํธ์ ์์ฃผ ์ข์ต๋๋ค.
How to use
๋ณธ๋ก ์ผ๋ก ๋์ด์์ ๋ ํ ์คํธ ํ๋ ์์ํฌ๋ก ์ด๋ป๊ฒ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋์ง ์์๋ณด๊ฒ ์ต๋๋ค. ์์ํ๊ธฐ ์ ์, ์์ ์ด ํ ์คํธ ์ฝ๋๋ฅผ ํด๋์ค๋ก ์์ฑํ๊ณ ์ถ๋ค๋ฉด UnitTest๋ฅผ, ๋จ์ ํจ์๋ก ์์ฑํ๊ณ ์ถ๋ค๋ฉด Pytest๋ฅผ ์ด์ฉํ๋ ๊ฑธ ์ถ์ฒํฉ๋๋ค.
๋จผ์ UnitTest๋ฅผ ์ด์ฉํ์ฌ Class-like ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
Class-like์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ๊ธฐ ์ํด unittest ํจํค์ง์ TestCase๋ฅผ ์์ํฉ๋๋ค. TestCase ํด๋์ค์์ ์ ๊ณตํ๋ setUp ํจ์๋ ํ ์คํธ ์ฝ๋๋ฅผ ์์ํ ๋ ์ฒ์์ผ๋ก ์คํ๋๋ TestCase ํด๋์ค์ ์ฝ๋ฐฑ ํจ์์ ๋๋ค.
์ด๋ฌํ ์ฝ๋ฐฑ ํจ์๋ ์์์๋ ๋๊ฐ์ด ์ ์ฉ๋๋ฉฐ ๊ฐ ํ ์คํธ ์ผ์ด์ค์ ์ด๊ธฐ ์ค์ ์ด ํ์ํ ๊ฒฝ์ฐ์ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
UnitTest๋ assert๋ฅผ ์ํ assertArrayEquals ๋ฑ์ ํด๋์ค ๋ฉ์๋๋ฅผ ์ง์ํฉ๋๋ค.
๋ค์์ Pytest๋ฅผ ์ด์ฉํ Functional ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
OOP์ ์ต์ํ์ง ์์ผ์ ๋ถ๋ค์ด๋ผ๋ฉด ์คํ๋ ค ์ผ๋ฐ ํจ์๋ก ์์ฑํ๋ ๊ฒ์ด ๋ ๊น๋ํด ๋ณด์ผ ์๋ ์์ต๋๋ค. ์ด๋ ๊ฒ ์์ฑ๋ ํ ์คํธ ์ฝ๋๋ ์๋์ ๋ช ๋ น์ด๋ก ์คํํ ์ ์์ต๋๋ค.
$ pytest -v
๊ธฐ๋ณธ์ ์ผ๋ก pytest๋ ์ ๋์ฌ test_*.py ํ์ผ์ ์๋์ผ๋ก ์ฝ์ด ํด๋น ์ฝ๋๋ง์ ์คํํฉ๋๋ค. ๋ง์ฝ ๋ค๋ฅธ ํ์ผ๋ ๊ฐ์ด ๋๋ฆฌ๊ณ ์ ํ๋ค๋ฉด ๋๋ ํฐ๋ฆฌ๋ฅผ ์ง์ ํ๊ฑฐ๋ ๋ชจ๋ ์ด๋ฆ์ ๋ช ๋ ์ด ๋ค์ ๋ฃ์ด ์คํํ๋ฉด ๋ฉ๋๋ค. ๋ณ๋์ flag๋ ์ฌ์ฉํ์ง ์์ต๋๋ค.
FastAPI Test
FastAPI๋ก ์์ฑํ ์ฝ๋๋ ์ด๋ป๊ฒ ํ ์คํธํ ์ ์์๊น์? ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฒ์ ํ ์คํธ๋ integration Test (ํตํฉ ํ ์คํธ)๋ฅผ ํตํด ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ง์ ๊ฒ์ฆํฉ๋๋ค.
- ํ ์คํธ ํ๊ฒฝ์ผ๋ก ๊ตฌ์ฑ๋ Application ์คํ
- HttpClient ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ API ํธ์ถ ํ ์คํธ
ํตํฉ ํ ์คํธ๋ ์ ๋ ๊ฐ์ง ๊ณผ์ ์ ํตํด ํ ์คํธ๊ฐ ์งํ๋๋ฉฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ํฌํจ๋ ๋ชจ๋ ๋ฏธ๋ค์จ์ด๊ฐ ์ค๋น๋์ด ์์ด์ผ ํฉ๋๋ค. Java Spring์ ๊ฒฝ์ฐ์๋ Local Database๋ก H2๋ฅผ ์ฌ์ฉํ ์๋ ์๊ณ , FastAPI์ ๊ฒฝ์ฐ์๋ SQLite๋ฅผ ์ฌ์ฉํด๋ณผ ์๋ ์์ต๋๋ค.
ํ์ง๋ง ์ด ํฌ์คํธ์์๋ ์ค์ ๋ก ์ฌ๋ฌ๋ถ๋ค์ด ์์ฃผ ์ฌ์ฉํ๋ ๊ด๊ณํ DB๋ฅผ ์ง์ ์ฌ์ฉํด๋ณผ ๊ฒ์ด๋ฉฐ ๊ทธ ์ค์์๋ PostgreSQL์ ์ฌ์ฉํด์ ์์ ๋ฅผ ๋ณด์ฌ๋๋ฆด ๊ฒ์ ๋๋ค.
ํ ์คํธ ๋ชจ๋์ ๊ตฌ์กฐ๋ ์์ ๊ฐ์ต๋๋ค. test_memo.py์ ์ฐ๋ฆฌ๊ฐ ์ด์ ์ ์์ฑํ๋ Memo Application์ ๋ํ API ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํ ๊ฒ์ด๋ฉฐ conftest.py์๋ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ test fixture๋ฅผ ์์ฑํ ๊ฒ์ ๋๋ค.
์ฐ์ test/conftest.py์ ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ fixture๋ฅผ ์ ์ํฉ๋๋ค.
FastAPI๋ Starlette ํ๋ ์์ํฌ๋ฅผ ๊ธฐ๋ฐํ๊ณ ์์ผ๋ฏ๋ก Starlette์์ ์ ๊ณตํ๋ TestClient๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋จ, TestClient๋ requests ๋ํ๋์๋ฅผ ์ฌ์ฉํ๋ฏ๋ก requests ๋ํ๋์๋ฅผ ์ค์นํ ํ ์ด์ฉํด์ผ ํฉ๋๋ค.
conftest.py์ ๋ฑ๋ก๋ fixture๋ ๋ณ๋์ import ์์ด ๋ฐ๋ก ํ ์คํธ ํจ์์ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ฏธ๋ฆฌ ์ ์๋ fixture๋ก DB์ ๋ฐ์ดํฐ๋ฅผ ์์์ํจ ํ API๋ฅผ ํธ์ถํ์ ๋ ์ ๋๋ก ์กฐํ๊ฐ ๋๋์ง๋ฅผ ํ์ธํฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ , POST ๋ฉ์๋์ ๋ํด์๋ Request json์ ๋ฏธ๋ฆฌ ์ ์ํ๊ณ api๋ก ํธ์ถํฉ๋๋ค. ๋ง์ง๋ง์ผ๋ก ๋ฏธ๋ฆฌ ์ ์๋ fixture๋ก db์์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ ํ ๋ฐ์ดํฐ๊ฐ ๋ง๋์ง๋ฅผ ํ์ธํ๋ฉด ํ ์คํธ ์ฝ๋๊ฐ ์์ฑ๋ฉ๋๋ค.
ํ ์คํธ๊ฐ ์ ์ํ๋๊ณ ์์์ ๋ณผ ์ ์์ต๋๋ค.
AsyncClient Test
๋ฐฉ๊ธ ์ ๊น์ง์ ํ ์คํธ ์ฝ๋๋ ๋๊ธฐ ํด๋ผ์ด์ธํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ requests๋ฅผ ์ฌ์ฉํ์ฌ ๋๊ธฐ ํ ์คํธ๋ฅผ ์งํํ ๊ฒ์ ๋๋ค. ๋ง์ฝ, ๋น๋๊ธฐ ํจ์๋ก API ์ฝ๋๋ฅผ ์์ฑํ๋ค๋ฉด ๋น๋๊ธฐ์ ๋ํ ํ ์คํธ๊ฐ ํ์ํฉ๋๋ค.
์ด ๋๋ requests ๋ํ๋์๊ฐ ์๋ httpx ๋ํ๋์๋ฅผ ์ด์ฉํ์ฌ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด ๋ณผ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์๋ AsyncClient ์ธ์ test_db_session ๋ํ asyncpg CP๋ฅผ ์ด์ฉํ์ฌ async_session์ผ๋ก ํ ์คํธ๋ฅผ ์งํ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
pytest์์ ๋น๋๊ธฐ ํ ์คํธ ์ฝ๋๋ฅผ ์ฌ์ฉํ ๋๋ pytest-asyncio ๋ํ๋์๋ฅผ ๋ณ๋๋ก ์ค์นํ์ฌ์ผ ํฉ๋๋ค.
ํตํฉ ํ ์คํธ์์๋ LifespanManager๋ฅผ ์ฌ์ฉํ์ฌ FastAPI์ startup, shutdown ๋ฉ์๋๋ฅผ ๋์์ํฌ ์ ์๋๋ก ํด์ผ ํฉ๋๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด Connection Pool๊ณผ ๊ฐ์ ์์ ๋ค์ด ์งํ๋์ง ์์ ํ ์คํธ ์ฝ๋๊ฐ ์ ์ ๋์ํ์ง ์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ํ ์คํธ ์ฝ๋๊ฐ asyncio์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก fixture์ asyncio์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ถ๊ฐํด์ค๋๋ค. ํ์ด์ฌ์ ์ด๋ฒคํธ ๋ฃจํ์ ๋ํด์ ์ ๋ชจ๋ฅด์๊ฒ ๋ค๋ฉด ํ์ด์ฌ ๊ณต์ ๋ฌธ์ asyncio๋ฅผ ์ฐธ๊ณ ํด๋ณด์ธ์.
๋ชจ๋ ํ ์คํธ ํจ์์ pytest.mark.asyncio ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋ถ์ฌ์ค ๋ค async await ๊ตฌ๋ถ์ ์ฌ์ฉํ๋๋ก ํฉ๋๋ค. SQLAlchemy์ ๊ฒฝ์ฐ DB์์ ๋ฐ์ดํฐ ์กฐํ์ AsyncSession์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ SQLAlchemy 2.x ๋ฌธ๋ฒ์ ์ฌ์ฉํฉ๋๋ค.
์ ์์ ์ผ๋ก ๋์ํ๋ค๋ฉด ์์ ๊ฐ์ด passed๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค.
Layered Architrecture Test
Python์ UnitTest์์๋ ๊ฐ์ฒด๋ฅผ Mocking ํ ์ ์๋ Mock ํจ์๋ฅผ ์ง์ํฉ๋๋ค. ์ฐ๋ฆฌ๋ ์ด๋ฅผ ์ด์ฉํด์ ๋ง๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ํด์ง ์ํคํ ์ฒ์ ๋ง์ถฐ ํจ์๊ฐ ํธ์ถ๋๊ณ ์๋์ง๋ฅผ ์ ๋ ํ ์คํ ํ ์ ์์ต๋๋ค.
API Endpoint์ ๋ํ ํ ์คํธ๋ฅผ ์งํํ๋ ๊ฒฝ์ฐ FastAPI๊ฐ ๊ฐ์ง๊ณ ์๋ DI์ override๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ๋ํ๋์๋ฅผ Mockingํด ํ ์คํธ ํด๋ณผ ์ ์์ต๋๋ค.
๊ทธ๋ฐ ๋ค์ assert_called_once_with ํน์ assert_called_with ํจ์๋ฅผ ์ด์ฉํ์ฌ ํด๋น Layer์์ ํ์ ๋ ์ด์ด๊ฐ ์ ํธ์ถ๋๊ณ ์๋์ง๋ฅผ ํ ์คํธ ํด๋ณผ ์ ์์ต๋๋ค.
Dependency Injector๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ํด๋น ์ปจํ ์ด๋์ ์ ์๋ ๋ํ๋์๋ฅผ ์ ์ํ ํ override ํจ์๋ฅผ ์ด์ฉํด์ ํด๋น ๋ํ๋์๋ฅผ ๋ชจํนํ ์ ์์ต๋๋ค. ์ด ๋ Context Manager๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๋ง์น๋ฉฐ...
์ฌ๊ธฐ๊น์ง Pytest, UnitTest๋ฅผ ์ด์ฉํ์ฌ FastAPI๋ก ๋ง๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธ ํด๋ณผ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ดค์ต๋๋ค.
ํ์ด์ฌ์์๋ ํ๋ก์ธ์ค, ์ค๋ ๋ ์ธ์ ์ด๋ฒคํธ ๋ฃจํ์ธ asyncio๋ฅผ ์ด์ฉํ์ฌ ๋น๋๊ธฐ ๋งค์ปค๋์ฆ์ ์ฌ์ฉํด ๋์์ฑ ์ฒ๋ฆฌํ ์ ์๋ค๋ณด๋ ํ ์คํธ ์ฝ๋์ ๋ํด์๋ ์ด๋ฌํ ๋ถ๋ถ์ ์ ๊ฒฝ์จ์ค์ผ ํ๋ฉฐ ํนํ ์์กด์ฑ ์ฃผ์ ์ด ์ด๋ค์์ผ๋ก ์ด๋ค์ง๊ณ ์ด๋ป๊ฒ ์ฌ์ฉํด์ผ ํ๋์ง์ ๋ํ ์ถฉ๋ถํ ์ดํด์ ์์ง๊ฐ ํ์ํฉ๋๋ค.
์กฐ๊ธ ๋์น ์ฑ์ ๋ถ๋ค๋ ๊ณ์๊ฒ ์ง๋ง Layered Architrecture ๋ด์ง DDD๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ ๋ ํ ์คํธ๋ฅผ ์ด์ฉํด TDD๋ฅผ ํด๋ณผ ์๋ ์๊ฒ ๋ค ๋ผ๋ ์๊ฐ์ ํ์ จ์ ๊ฒ์ ๋๋ค. ๋ง์ต๋๋ค. ์ด๋ค ๋ํ๋์๋ฅผ ์ด์ฉํด์ ์ถฉ๋ถํ Python ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์๋ ๋ค์ํ ๊ฐ๋ฐ ๋ฐฉ๋ฒ๋ก ์ ์ด์ฉํด ๋ณผ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ behave ๋ํ๋์๋ฅผ ์ฌ์ฉํ๋ค๋ฉด Given When Then ํจํด์ BDD๋ฅผ ํด๋ณผ ์๋ ์๋๋ฐ์. ์ด๋ค ์ปจํ ์ธ ์ ๋ํด์๋ ์ฐจํ ๋ค๋ฅธ ํฌ์คํธ์์ ๋ง๋๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.