# main.py
@app.get("/todos", status_code=200)
def get_todos_handler(
order: str | None = None,
session: Session = Depends(get_db),
# 타입 힌트
) -> ToDoListSchema:
# 타입 힌트
todos: List[ToDo] = get_todos(session=session)
if order and order == "DESC":
return ToDoListSchema(
# todos: List[ToDoSchema] response.py에 이렇게 리스트 형식으로 작성해놨기 때문에 동일하게 []로 작성
todos=[ToDoSchema.model_validate(todo) for todo in todos[::-1]]
)
return ToDoListSchema(
todos=[ToDoSchema.model_validate(todo) for todo in todos]
)

# test_main.py
def test_get_todos():
# 밑의 3줄은 테스트의 기본 구조
response = client.get("/todos")
assert response.status_code == 200
assert response.json() == {
# 기존에 todos라는 list로 반환을 했기에 리스트 형식으로 작성해준다.
"todos": [
{"id": 1, "contents": "FastAPI Section 0", "is_done":True},
{"id": 2, "contents": "FastAPI Section 1", "is_done": True},
{"id": 3, "contents": "FastAPI Section 2", "is_done": False},
]
}
(todos) PS C:\Users\user\Desktop\hyun\fastpai\todos\src> pytest
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.14.6, pytest-9.1.1, pluggy-1.6.0
rootdir: C:\Users\user\Desktop\hyun\fastpai\todos\src
plugins: anyio-4.13.0
collected 2 items
tests\test_main.py .. [100%]
================================================================ warnings summary =================================================================
..\Lib\site-packages\fastapi\testclient.py:1
C:\Users\user\Desktop\hyun\fastpai\todos\Lib\site-packages\fastapi\testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
schema\response.py:6
C:\Users\user\Desktop\hyun\fastpai\todos\src\schema\response.py:6: PydanticDeprecatedSince20: Support for class-based `config` is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.13/migration/
class ToDoSchema(BaseModel):
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================================================== 2 passed, 2 warnings in 0.75s ==========================================================
(todos) PS C:\Users\user\Desktop\hyun\fastpai\todos\src> pytest
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.14.6, pytest-9.1.1, pluggy-1.6.0
rootdir: C:\Users\user\Desktop\hyun\fastpai\todos\src
plugins: anyio-4.13.0
collected 2 items
tests\test_main.py .. [100%]
================================================================ warnings summary =================================================================
..\Lib\site-packages\fastapi\testclient.py:1
C:\Users\user\Desktop\hyun\fastpai\todos\Lib\site-packages\fastapi\testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
schema\response.py:6
C:\Users\user\Desktop\hyun\fastpai\todos\src\schema\response.py:6: PydanticDeprecatedSince20: Support for class-based `config` is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.13/migration/
class ToDoSchema(BaseModel):
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================================================== 2 passed, 2 warnings in 1.13s =========================================================
- 기존 /todos path로 작성된 코드로 조회를 하면 swagger와 같은 데이터들이 조회가 된다.
- 그리고 테스트 코드를 위와 같이 작성하고 pytest를 실행하면 위와 같이 테스트 함수가 성공했다는 것을 알 수 있다.
- 밑에 2 passed는 실제 동작한 함수의 개수이다.
# test_main.py
def test_get_todos():
# 밑의 3줄은 테스트의 기본 구조
response = client.get("/todos")
assert response.status_code == 200
assert response.json() == {
# 기존에 todos라는 list로 반환을 했기에 리스트 형식으로 작성해준다.
"todos": [
{"id": 1, "contents": "FastAPI Section 0", "is_done":True},
{"id": 2, "contents": "FastAPI Section 1", "is_done": True},
{"id": 3, "contents": "FastAPI Section 2", "is_done": True},
]
}
기존에 id가 3이었던 데이터 부분의 is_done 값을 False에서 True로 바꾸고 pytest를 실행하면
(todos) PS C:\Users\user\Desktop\hyun\fastpai\todos\src> pytest
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.14.6, pytest-9.1.1, pluggy-1.6.0
rootdir: C:\Users\user\Desktop\hyun\fastpai\todos\src
plugins: anyio-4.13.0
collected 2 items
tests\test_main.py .F [100%]
==================================================================== FAILURES =====================================================================
_________________________________________________________________ test_get_todos __________________________________________________________________
def test_get_todos():
# order = ASC
response = client.get("/todos")
assert response.status_code == 200
> assert response.json() == {
"todos": [
{"id": 1, "contents": "FastAPI Section 0", "is_done":True},
{"id": 2, "contents": "FastAPI Section 1", "is_done": True},
{"id": 3, "contents": "FastAPI Section 2", "is_done": True},
]
}
E AssertionError: assert {'todos': [{'...one': False}]} == {'todos': [{'...done': True}]}
E
E Differing items:
E {'todos': [{'id': 1, 'contents': 'FastAPI Section 0', 'is_done': True}, {'id': 2, 'contents': 'FastAPI Section 1', '[3[0m
E
E ...Full output truncated (2 lines hidden), use '-vv' to show
tests\test_main.py:20: AssertionError
-------------------------------------------------------------- Captured stdout call ---------------------------------------------------------------
2026-07-06 17:21:09,947 INFO sqlalchemy.engine.Engine SELECT DATABASE()
2026-07-06 17:21:09,947 INFO sqlalchemy.engine.Engine [raw sql] {}
2026-07-06 17:21:09,949 INFO sqlalchemy.engine.Engine SELECT @@sql_mode
2026-07-06 17:21:09,950 INFO sqlalchemy.engine.Engine [raw sql] {}
2026-07-06 17:21:09,951 INFO sqlalchemy.engine.Engine SELECT @@lower_case_table_names
2026-07-06 17:21:09,951 INFO sqlalchemy.engine.Engine [raw sql] {}
2026-07-06 17:21:09,953 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2026-07-06 17:21:09,955 INFO sqlalchemy.engine.Engine SELECT todo.id, todo.contents, todo.is_done
FROM todo
2026-07-06 17:21:09,955 INFO sqlalchemy.engine.Engine [generated in 0.00030s] {}
2026-07-06 17:21:09,963 INFO sqlalchemy.engine.Engine ROLLBACK
---------------------------------------------------------------- Captured log call ----------------------------------------------------------------
INFO sqlalchemy.engine.Engine:base.py:1848 SELECT DATABASE()
INFO sqlalchemy.engine.Engine:base.py:1848 [raw sql] {}
INFO sqlalchemy.engine.Engine:base.py:1848 SELECT @@sql_mode
INFO sqlalchemy.engine.Engine:base.py:1848 [raw sql] {}
INFO sqlalchemy.engine.Engine:base.py:1848 SELECT @@lower_case_table_names
INFO sqlalchemy.engine.Engine:base.py:1848 [raw sql] {}
INFO sqlalchemy.engine.Engine:base.py:2712 BEGIN (implicit)
INFO sqlalchemy.engine.Engine:base.py:1848 SELECT todo.id, todo.contents, todo.is_done
FROM todo
INFO sqlalchemy.engine.Engine:base.py:1848 [generated in 0.00030s] {}
INFO sqlalchemy.engine.Engine:base.py:2715 ROLLBACK
================================================================ warnings summary =================================================================
..\Lib\site-packages\fastapi\testclient.py:1
C:\Users\user\Desktop\hyun\fastpai\todos\Lib\site-packages\fastapi\testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
schema\response.py:6
C:\Users\user\Desktop\hyun\fastpai\todos\src\schema\response.py:6: PydanticDeprecatedSince20: Support for class-based `config` is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.13/migration/
class ToDoSchema(BaseModel):
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
============================================================= short test summary info =============================================================
FAILED tests/test_main.py::test_get_todos - AssertionError: assert {'todos': [{'...one': False}]} == {'todos': [{'...done': True}]}
===================================================== 1 failed, 1 passed, 2 warnings in 1.35s ====================================================
이렇게 실패가 나온다.
tests\test_main.py:16 (test_get_todos)
{'todos': [{'contents': 'FastAPI Section 0', 'id': 1, 'is_done': True},
{'contents': 'FastAPI Section 1', 'id': 2, 'is_done': True},
{'contents': 'FastAPI Section 2', 'id': 3, 'is_done': False}]} != {'todos': [{'contents': 'FastAPI Section 0', 'id': 1, 'is_done': True},
{'contents': 'FastAPI Section 1', 'id': 2, 'is_done': True},
{'contents': 'FastAPI Section 2', 'id': 3, 'is_done': True}]}
만약 위의 로그만으로 뭐가 잘못된지 잘 모르겠다면

이 부분을 눌러서 실행을 시키면 위와 같이 로그에 어떤 부분이 틀렸는지 쉽게 볼 수 있다.
데이터 역정렬해서 테스트해보기
def test_get_todos():
# order = ASC
response = client.get("/todos")
assert response.status_code == 200
assert response.json() == {
"todos": [
{"id": 1, "contents": "FastAPI Section 0", "is_done":True},
{"id": 2, "contents": "FastAPI Section 1", "is_done": True},
{"id": 3, "contents": "FastAPI Section 2", "is_done": False},
]
}
# order = DESC
response = client.get("/todos?order=DESC")
assert response.status_code == 200
assert response.json() == {
"todos": [
{"id": 3, "contents": "FastAPI Section 2", "is_done": False},
{"id": 2, "contents": "FastAPI Section 1", "is_done": True},
{"id": 1, "contents": "FastAPI Section 0", "is_done": True},
]
}
- 윗쪽은 기존 코드 밑은 그 기존 코드에서 역정렬 내림차순한 데이터로 return되는 테스트하는 함수로 만든다.
- 쿼리스트링 부분에 기존 path 뒤에 ?order=DESC를 넣어준다.
(todos) PS C:\Users\user\Desktop\hyun\fastpai\todos\src> pytest
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.14.6, pytest-9.1.1, pluggy-1.6.0
rootdir: C:\Users\user\Desktop\hyun\fastpai\todos\src
plugins: anyio-4.13.0
collected 2 items
tests\test_main.py .. [100%]
================================================================ warnings summary =================================================================
..\Lib\site-packages\fastapi\testclient.py:1
C:\Users\user\Desktop\hyun\fastpai\todos\Lib\site-packages\fastapi\testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
from starlette.testclient import TestClient as TestClient # noqa
schema\response.py:6
C:\Users\user\Desktop\hyun\fastpai\todos\src\schema\response.py:6: PydanticDeprecatedSince20: Support for class-based `config` is deprecated, use ConfigDict instead. Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at https://errors.pydantic.dev/2.13/migration/
class ToDoSchema(BaseModel):
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
========================================================== 2 passed, 2 warnings in 1.21s =========================================================
그럼 위와 같이 테스트가 잘 통과했다고 확인할 수 있다.
'FastAPI > 테스트 코드' 카테고리의 다른 글
| GET 단일 조회 API (0) | 2026.07.07 |
|---|---|
| PyTest Fixture (0) | 2026.07.07 |
| PyTest Mocking (0) | 2026.07.07 |
| PyTest란? (0) | 2026.07.01 |
| 테스트 코드란? (0) | 2026.07.01 |