FastAPI/테스트 코드

GET 전체 조회 API

hyunai 2026. 7. 6. 17:32
# 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