inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

실전! FastAPI 입문

(실습) 테스트 코드 - DELETE API

AssertionError: Expected 'undone' to be called once. Called 0 times.

해결된 질문

561

daniel

작성한 질문수 58

0

def test_update_todo(client, mocker):
    '''
    특정 객체 update 테스트
    # 200
    '''
    mocker.patch(
        "main.get_todo_by_todo_id",
        return_value= ToDo(id=1, contents="todo", is_done=True),
    )
    undone = mocker.patch.object(ToDo, "undone")
    mocker.patch(
        "main.update_todo",
        return_value=ToDo(id=1, contents="todo", is_done=False),
    )

    # API 호출 여부
    response = client.patch("/todos/1", json={"is_done": False})

    undone.assert_called_once_with()

    assert response.status_code == 200  # 성공
    assert response.json() == {"id": 1, "contents": "todo", "is_done": False} # 성공

    # Resetting the mock before the next scenario
    # 404
    mocker.patch(
        "main.get_todo_by_todo_id",
        return_value=None,
    )

    response = client.patch("/todos/1", json={"is_done": True})

    assert response.status_code == 404
    assert response.json() == {"detail": "ToDo Not Found"}


pytest 시 아래와 같은 에러가 발생합니다. 강사님 코드와 상이한 부분도 없고 로직도 맞게 작성한 것 같은데 undone이 한번도 호출이 되지 않았다고 합니다.

During handling of the above exception, another exception occurred:

client = <starlette.testclient.TestClient object at 0xffff89948640>, mocker = <pytest_mock.plugin.MockerFixture object at 0xffff89004e50>

    def test_update_todo(client, mocker):
        '''
        특정 객체 update 테스트
        # 200
        '''
        mocker.patch(
            "main.get_todo_by_todo_id",
            return_value= ToDo(id=1, contents="todo", is_done=True),
        )
        undone = mocker.patch.object(ToDo, "undone")
>       print(undone.assert_called_once_with())
E       AssertionError: Expected 'undone' to be called once. Called 0 times.

tests/test_main.py:106: AssertionError
========================================================== short test summary info ==========================================================
FAILED tests/test_main.py::test_update_todo - AssertionError: Expected 'undone' to be called once. Called 0 times.
======================================================== 1 failed, 5 passed in 0.18s ========================================================
# 

python 리팩토링 orm FastAPI pytest

답변 1

0

신동현

안녕하세요! 테스트 코드만 봐서는 정확한 원인 파악이 힘들 것 같은데 혹시 handler 코드도 첨부해주실 수 있으실까요?

0

daniel

from fastapi import FastAPI, Body, HTTPException, Depends
from typing import Optional, List

from database.connection import get_db
from database.repository import get_todos, get_todo_by_todo_id, create_todo, update_todo, delete_todo
from database.orm import ToDo

from sqlalchemy.orm import Session

from schema.response import ToDoListSchema, ToDoSchema
from schema.request import CreateToDoRequest

app = FastAPI()

@app.get("/")
def health_check_handler():
    return {"ping": "pong"}


todo_data = {
    1: {
        "id": 1,
        "contents": "실전! FastAPI 섹션 0 수강",
        "is_done": True
    },
    2: {
        "id": 2,
        "contents": "실전! FastAPI 섹션 1 수강",
        "is_done": False
    },
    3: {
        "id": 3,
        "contents": "실전! FastAPI 섹션 2 수강",
        "is_done": False
    },
}


@app.get("/todos", status_code=200)
# def get_todos_handler(order: str | None = None):
def get_todos_handler(
        order: Optional[str] = None,
        session: Session = Depends(get_db),
):
    todos: List[ToDo] = get_todos(session=session)
    # ret = list(todo_data.values())
    if order and order == "DESC":
        return ToDoListSchema(
            todos=[
                ToDoSchema.from_orm(todo) for todo in todos[::-1]
            ]
        )
    return ToDoListSchema(
        todos=[
            ToDoSchema.from_orm(todo) for todo in todos
        ]
    )


@app.get("/todos/{todo_id}", status_code=200)
def get_todos_handler(
        todo_id: int,
        session: Session = Depends(get_db),
):
    todo: Optional[ToDo] = get_todo_by_todo_id(session=session, todo_id=todo_id)

    # todo = todo_data.get(todo_id)
    if todo:
        return ToDoSchema.from_orm(todo)

    raise HTTPException(status_code=404, detail="ToDo Not Found")


@app.post("/todos", status_code=201)
def create_todo_handler(
        request: CreateToDoRequest,
        session: Session = Depends(get_db),
) -> ToDoSchema:
    '''
    pydantic -> orm -> database
    '''
    todo: ToDo = ToDo.create(request=request)  # id=None

    todo: ToDo = create_todo(
        session=session,
        todo=todo,
    )  # id=int

    return ToDoSchema.from_orm(todo)

    # todo_data[request.id] = request.dict()
    # return todo_data[request.id]


@app.patch("/todos/{todo_id}", status_code=200)
def update_todo_handler(
        todo_id: int,
        # ... : required, embeded : key 값
        is_done: bool = Body(..., embeded=True, example=True),
        session: Session = Depends(get_db),
):
    todo: Optional[ToDo] = get_todo_by_todo_id(session=session, todo_id=todo_id)
    # todo: ToDo | None = get_todo_by_todo_id(session=session, todo_id=todo_id)

    # todo = todo_data.get(todo_id)
    if todo:
        todo.done() if is_done else todo.undone()
        todo: ToDo = update_todo(session=session, todo=todo)
        return ToDoSchema.from_orm(todo)

    raise HTTPException(status_code=404, detail="ToDo Not Found")


@app.delete("/todos/{todo_id}", status_code=204)
def delete_todo_handler(
        todo_id: int,
        session: Session = Depends(get_db),
):
    todo: Optional[ToDo] = get_todo_by_todo_id(session=session, todo_id=todo_id)

    if not todo:
        raise HTTPException(status_code=404, detail="ToDo Not Found")

    # delete
    delete_todo(session=session, todo_id=todo_id)

핸들러 함수입니다!

0

신동현

보통 mocking을 이용한 테스트에서 자주 발생하는 실수는 mocking 하는 메소드 이름에 오타가 있는 경우가 있습니다. 예를 들어, abc 메소드를 mocking 해야 되는데 ab로 mocking 하는 경우입니다. 다만 첨부해주신 코드상 오타가 있어 보이진 않는데요.

이런 경우에는 코드를 한 줄씩 보면서 개별 코드가 잘 동작하는지 디버깅을 해봐야 할 것 같습니다.

먼저 handler 코드의 if todo 분기문이 잘 동작하는지 그래서 분기문 아래의 코드가 잘 실행되는지 확인해야 할 것 같습니다.

현재 코드에서 if todo 분기문이 잘 동작한다면 응답의 상태 코드가 200을 리턴해야 합니다. 테스트 코드에서 undone.assert_called_once_with() 부분을 주석 처리하고 테스트 코드를 동작했을 때 어떻게 결과가 출력되는지 확인 부탁드립니다.

 

FasAPI Swagger UI에서 단일조회 todo_id 를 사용했는데, 입력값에 1을 넣으니 오류가 뜹니다 ㅠ

0

92

2

DB 질문

0

81

2

Post API 강의 질문

0

56

2

post 작성 오류

0

107

3

uvicorn 종료 문제

0

441

2

왜 return타입이 ToDo라는 스트링인가요?

0

131

2

ORM 연관관계

0

111

2

Oracle DB 연결과 관련해 질문이 있습니다..

0

154

2

섹션 2, 3의 PATCH API의 차이점

0

132

1

ORM 테이블 생성

0

121

1

테스트 코드 오류

0

177

1

orm relationship 정의 중 해당 에러 발생 시 어떻게 고쳐야 하는걸까요

0

215

3

FastAPI 폴더 구조에 대해 질문이 있습니다.

0

415

2

질문 있습니다.

0

162

2

ORM 개념

0

134

1

mysql root 비밀번호 변경관련

0

302

2

[질문] patch API

0

152

3

디자인 패턴

0

129

1

main.py 리로드문제

0

235

2

인터프리터 오류

0

288

4

로그 저장에 대한 질문입니다.

0

102

1

Internal Server Error

0

245

2

PATCH API - 수정

0

201

2

파이참 임포트 문제

0

266

1