FastAPI/테스트 코드

PyTest Mocking

hyunai 2026. 7. 7. 14:56
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},
        ]
    }
  • 이렇게 테스트 코드를 작성을 하다보면 함수가 두개가 있어 데이터 조회를 두번하게 된다.
  • 테스트 코드를 작성하다보면 이런 식으로 API를 여러 번 호출하게 되는데 이럴 때마다 실제 데이터베이스에 데이터 요청이 가게 된다.
  • 지금 같은 경우는 데이터를 조회하는 요청이기 때문에 크게 문제가 없을 수 있지만 데이터를 생성하거나 업데이트하거나 삭제하는 데이터를 변경하는 경우에는 실제 데이터베이스에 영향을 주게 된다.
  • 이때 사용되는 기술이 mocking이라는 기술이다.
    • 실제 데이터베이스에 요청을 하지 않지만 마치 데이터 베이스 요청을 한 것처럼 속이는, 흉내 내는 그런 기술
    • 그렇다고 꼭 반드시 사용해야 되는 기술은 아니다.
    • 예를 들어, 외부 API를 이용한다거나 데이터베이스 요청을 한다거나 하는 오래 걸리는 어떤 동작이 있을 때 그 부분을 mocking을 사용해서 대체를 하게 되면 실제로 그 부분이 동작을 하지 않아서 테스트 코드가 훨씬 빨리 동작하게 된다.
    • 만약에 mocking을 사용하지 않고 매번 테스트마다 실제로 데이터베이스를 조회하게 됐을 때 데이터가 많다면 이 테스트 코드가 5~10분 1시간까지도 동작할수가 있다.
    • 테스트 코드의 목적이 그런식으로 실제 데이터를 검증하는 것은 아니기 때문에 mocking이라는 기술을 사용하게 된다.

라이브러리 install

pip install pytest-mock
# 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(mocker):
    # order = ASC

    # mocker를 쓰며 실제로 이 함수를 동작시키는 것이 아니라 이 함수가 동작한 것처럼 속이겠다 라는 의미
    # 그래서 DB에 들리지 않고 그냥 밑의 return_value 값으로 반환이 돼어 테스트하게 된다.
    mocker.patch("main.get_todos", return_value=[
        ToDo(id=1, contents="FastAPI Section 0", is_done=True),
        ToDo(id=2, contents="FastAPI Section 1", is_done=False),
    ])
    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": False},
        ]
    }

    # order = DESC
    response = client.get("/todos?order=DESC")
    assert response.status_code == 200
    assert response.json() == {
        "todos": [
            {"id": 2, "contents": "FastAPI Section 1", "is_done": False},
            {"id": 1, "contents": "FastAPI Section 0", "is_done": True},
        ]
    }
  • mocker를 쓸 함수 파라미터에 인자를 주입시켜 mocker를 작성해준다.
  • mocking은 실제 코드가 동작하기 전에 적용을 해줘야 한다.
  • 그래서 mocking을 하는게 뭐냐면 특정 함수를 가로채서 원하는 값을 대신 반환하게 만드는 것이다.(return_value 값으로 반환을 해준다.)
    • DB, 외부 API 등 내 코드가 아닌 것들을 가짜로 대체해서 내 로직만 순수하게 테스트할 수 있게 만들어주는 것
  • main에 있는 get_todos를 모킹할거다. 라고 작성해준것
(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, mock-3.15.1
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.77s =========================================================

그럼 이와 같이 테스트가 잘 된다.

'FastAPI > 테스트 코드' 카테고리의 다른 글

GET 단일 조회 API  (0) 2026.07.07
PyTest Fixture  (0) 2026.07.07
GET 전체 조회 API  (0) 2026.07.06
PyTest란?  (0) 2026.07.01
테스트 코드란?  (0) 2026.07.01