fast-api s3 upload test

x·2021년 4월 14일
0

s3 upload 테스트를 view 테스트로 하는 이유

실제 운영 환경에서 file upload API가 호출되면 담당하는 view에서 해당 file을 받고 boto3에서 제공하는 메서드까지 전달해야 한다. 결국 테스트 코드에서 file을 생성하거나 읽어와서 실제 s3 버킷의 지정된 경로에 저장되는지 확인해야 한다.
그리고 확인되었다면 patch 데코레이터를 써서 s3 upload 메서드를 mocking하여 불필요한 업로드를 막는 게 좋다.

단위 테스트를 통해 각각의 모듈을 독립적으로 테스트해도 되지만 어차피 API 호출 시 file을 받아서 s3 upload를 확인해야하므로 통합 테스트인 view 테스트를 하는 게 낫다.

view

@router.post(path="/upload")
def upload(
    user_id: str = Header(None),
    image: Optional[UploadFile] = File(None),
    session: Session = Depends(get_db),
):
    user_id = AuthValidator.validate_user_id(user_id)
    if not user_id:
        return Fail

    if not video_call_id:
        return Fail

    UploadUseCase(session=session).execute(
        dto=UploadDto(
            user_id=user_id, image=image
        )
    )

    return Success

use case

view에서 받은 file 등 데이터를 s3 upload까지 전달하는 로직 작성

s3 upload method

ACL은 엑세스 제어 목록

def upload(self, file_obj: str, bucket_name: str, key: str):
    s3 = boto3.client(service_name="s3")
    s3.upload(
        file_obj, bucket_name, key, ExtraArgs={"ACL": "public-read"}
    )

실제 이미지 파일 생성해서 업로드 테스트하기

ImagePillow 라이브러리를 설치해야 한다

def test_upload_img_file(
    client: TestClient
):
    user_id = 1

    url = app.url_path_for("upload")

    headers = {"user-id": str(user_id)}

    file = io.BytesIO()
    image = Image.new("RGBA", size=(1, 1), color=(0, 0, 0))
    image.save(file, "png")
    file.name = "test.png"
    file.seek(0)
    files = {"image": file}

    response = client.post(
        url,
        headers=headers,
        files=files,
    )

    assert response.status_code == 200

S3 client mocking해서 테스트하기

pytest patch 데코레이터를 활용해 s3 upload를 담당하는 메서드를 mocking한다
mock은 실제 upload를 하지 않으므로 아무 동작도 하지 않아도 된다. return_value는 None으로 정했다.

@patch("{path}.{class}.upload")
def test_upload_img_file(
    upload, client: TestClient
):
    upload.return_value = None

    user_id = 1

    url = app.url_path_for("upload")

    headers = {"user-id": str(user_id)}

    file = io.BytesIO()
    # image = Image.new("RGBA", size=(1, 1), color=(0, 0, 0))
    # image.save(file, "png")
    file.name = "test.png"
    file.seek(0)
    files = {"image": file}

    response = client.post(
        url,
        headers=headers,
        files=files,
    )

    assert response.status_code == 200

image file 생성하지 않고 읽어서 테스트하기

프로젝트 내 혹은 외부에 이미지를 저장해놓고 읽어서 upload하는 방법.
단점 : 테스트하려는 모두가 이미지를 로컬 컴퓨터에 갖고 있어야 함.

def test_should_success(client: TestClient, test_session):
    user_id = 1

    url = app.url_path_for("upload")

    headers = {"user-id": str(user_id)}
    file_ = {
        "image": (
            "1080.png",
            open("/Users/name/src/project/image.png", "rb"),
            "image/png",
        )
    }

    response = client.post(
        url,
        headers=headers,
        files=file_,
    )

    assert response.status_code == 200

postman에서 호출

0개의 댓글