[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๋ฅผ ํ•ด๋ณผ ์ˆ˜๋„ ์žˆ๋Š”๋ฐ์š”. ์ด๋“ค ์ปจํ…์ธ ์— ๋Œ€ํ•ด์„œ๋Š” ์ฐจํ›„ ๋‹ค๋ฅธ ํฌ์ŠคํŠธ์—์„œ ๋งŒ๋‚˜๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

๋ฐ˜์‘ํ˜•
TAGS.

Tistory Comments