• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

TODO 테스트 코드 작성

23.09.05 01:10 작성 조회수 186

0

저의 경우는 아래와 같이 해주어야 테스트 코드가 통과됩니다. 그 이후는 sql 도커의 데이터가 5개가 있어서 실제는 5개이기 때문이라 생각되어지는데요.

강의를 보면서 테스트코드 감을 못잡앗는데, 궁금한 점은 테스트 코드에서 user 변수에다가 id1 이고 test란 이름의 유저네임을 넣어주고 거기다가, todos를 목업으로 2개를 넣어줬는데, 왜 마지막에 assert할때는 user.todos에 넣어준 배열로 하는게 아닌 실제 db들어간 5개의 데이터로 assert로 체크하더라구요.

 

그러면 아래 mocker patch해준 부분이 제대로 안먹는건가요? 흠..

user = User(id=1, username="test", password="hashed")
user.todos = [
    ToDo(id=1, contents="FastAPI Section 0", is_done=True),
    ToDo(id=2, contents="FastAPI Section 1", is_done=False),
]

mocker.patch.object(UserRepository, "get_user_by_username", return_value=user)

 

def test_get_todos(client, mocker):
    access_token: str = UserService().create_jwt(username="test")
    headers = {"Authorization": f"Bearer {access_token}"}

    user = User(id=1, username="test", password="hashed")
    user.todos = [
        ToDo(id=1, contents="FastAPI Section 0", is_done=True),
        ToDo(id=2, contents="FastAPI Section 1", is_done=False),
    ]

    mocker.patch.object(UserRepository, "get_user_by_username", return_value=user)

    # order=ASC
    response = client.get("/todos", headers=headers)
    assert response.status_code == 200
    assert response.json() == {
        "todos": [
            {"id": 1, "contents": "FastAPI Section 0", "is_done": True},
            {"id": 2, "contents": "FastAPI Section 0", "is_done": True},
            {"id": 3, "contents": "string", "is_done": True},
            {"id": 4, "contents": "string", "is_done": True},
            {"id": 5, "contents": "string", "is_done": True},
        ]
    }

    # order=DESC
    response = client.get("/todos?order=DESC", headers=headers)
    assert response.status_code == 200
    assert response.json() == {
        "todos": [
            {"id": 5, "contents": "string", "is_done": True},
            {"id": 4, "contents": "string", "is_done": True},
            {"id": 3, "contents": "string", "is_done": True},
            {"id": 2, "contents": "FastAPI Section 0", "is_done": True},
            {"id": 1, "contents": "FastAPI Section 0", "is_done": True},
        ]
    }

답변 1

답변을 작성해보세요.

0

안녕하세요. 말씀하신 것처럼 mocking이 제대로 동작하고 있지 않고 있을 가능성이 높은 것 같습니다. 보통 오타가 있는 경우에 mocking이 동작하지 않을 수 있는데요.

router 코드도 같이 첨부해주시면 더 자세하게 디버깅을 도와드릴 수 있을 것 같습니다.

luke님의 프로필

luke

질문자

2023.09.05

아래가 라우터 코드이며 깃허브에 있으신 코드와 동일합니다만 저는 왜 실제 db로 되고 목킹이 제대로 안되는지 모르겠네요 . 보통 테스트 코드는 위의 글처럼 2개로 했으면 2개만 검증하고 넘어가는게 맞는걸까요? 그렇게 되어야 할거라고 생각되네요 실제 db는 투두가 100개 천개가 될수도 잇는데 저렇게 하드코딩해서 테스트 검증하는건 이상할거라 생각되어서요.

@router.get("", status_code=200)
def get_todos_handler(
    access_token: str = Depends(get_access_token),
    user_service: UserService = Depends(),
    order: str | None = None,
    user_repo: UserRepository = Depends(),
    todo_repo: ToDoRepository = Depends(),
) -> ToDoListSchema:
    username: str = user_service.decode_jwt(access_token=access_token)
    user: User | None = user_repo.get_user_by_username(username=username)
    if not user:
        raise HTTPException(status_code=404, detail="User Not Found")

    todos: List[ToDo] = todo_repo.get_todos()

    if order and order == "DESC":
        return ToDoListSchema(
            todos=[ToDoSchema.model_validate(todo) for todo in todos[::-1]]
        )
    return ToDoListSchema(todos=[ToDoSchema.model_validate(todo) for todo in todos])

안녕하세요. 강의와 동일하게 동작하기 위해서는 router 코드에 todos: List[ToDo] = todo_repo.get_todos() 부분을 todos: List[ToDo] = user.todos 로 바꾸어주셔야 합니다.

eager-loading을 이용하여 user 테이블에서 todo 테이블을 JOIN하여 가져온 뒤, todos를 바로 사용하고 있습니다.

자세한 내용은 섹션 5: (실습) JWT 사용 강의를 참고 부탁드립니다 :D

이어서 질문주신 테스트에서 데이터베이스의 데이터를 검증하는 것과 mocking 하는 것의 차이는 테스트의 목적에 따라 결정하시면 됩니다.

현업에서는 테스트 코드에서 실제 production 환경의 데이터베이스에 테스트를 수행하는 경우는 없고, 보통 docker를 이용해서 테스트를 위한 데이터베이스를 구성하여 데이터를 저장하고 출력하는 테스트를 하는 경우가 있는데 이를 intergration test라고 부릅니다.

intergration test는 다양한 시스템 간의 통합을 검증하는 절차인데, 실제 데이터베이스에 I/O를 발생시키기 때문에 intergration test가 많아지면 전체적인 테스트 동작 시간이 오래 걸립니다.

이에 따라 intergration test가 굳이 필요하지 않는 경우에는 해당 과정을 mocking으로 대체하곤 합니다.

본 강의에서도 intergration test가 필요하지 않다고 판단하여 데이터베이스 I/O에 대한 부분은 모두 mocking으로 처리하였습니다.