FastAPI 실습 코드
라이브러리 설치
Hello World : JSON 응답
테스트 수행
자동화된 테스트를 수행합니다.
pytest를 활용한 테스트
entry.py 파일과 같은 경로에 test_entry.py 파일을 둡니다.
test_entry.py
import pytest
from fastapi.testclient import TestClient
from entry import app
# TestClient 인스턴스 생성
client = TestClient(app)
def test_index():
"""메인 페이지 테스트"""
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"Hello": "World!"}
def test_article_detail():
"""아티클 상세 페이지 테스트"""
response = client.get("/articles/100/")
assert response.status_code == 200
data = response.json()
assert data["id"] == 100
def test_post_detail():
"""포스트 상세 페이지 테스트 - 정상적인 ID"""
# 1-100 범위의 유효한 post_id로 테스트
test_ids = [1, 42, 50, 99, 100]
for post_id in test_ids:
response = client.get(f"/posts/{post_id}/")
assert response.status_code == 200
data = response.json()
assert data["id"] == post_id
assert data["title"] == f"포스팅 #{post_id}"
assert data["content"] == "..."
def test_post_detail_invalid_id():
"""포스트 상세 페이지 테스트 - 잘못된 ID 타입"""
# 문자열을 전달하면 422 Unprocessable Entity 에러 발생
response = client.get("/posts/abc/")
assert response.status_code == 422
# 에러 메시지 확인
error_data = response.json()
assert "detail" in error_data
assert len(error_data["detail"]) > 0
assert error_data["detail"][0]["type"] == "int_parsing"
def test_post_detail_float_id():
"""포스트 상세 페이지 테스트 - 실수형 ID"""
# 실수를 전달해도 422 에러
response = client.get("/posts/3.14/")
assert response.status_code == 422
def test_nonexistent_endpoint():
"""존재하지 않는 엔드포인트 테스트"""
response = client.get("/nonexistent/")
assert response.status_code == 404
def test_post_detail_edge_cases():
"""포스트 상세 페이지 테스트 - 경계값"""
# 유효한 경계값 (1, 100)
response = client.get("/posts/1/")
assert response.status_code == 200
assert response.json()["id"] == 1
response = client.get("/posts/100/")
assert response.status_code == 200
assert response.json()["id"] == 100
# 무효한 경계값 테스트
# 0 (1 미만)
response = client.get("/posts/0/")
assert response.status_code == 404
assert "1 이상 100 이하" in response.json()["detail"]
# 101 (100 초과)
response = client.get("/posts/101/")
assert response.status_code == 404
assert "1 이상 100 이하" in response.json()["detail"]
# 음수
response = client.get("/posts/-1/")
assert response.status_code == 404
assert "1 이상 100 이하" in response.json()["detail"]
# 매우 큰 수
large_id = 9999999999
response = client.get(f"/posts/{large_id}/")
assert response.status_code == 404
assert "1 이상 100 이하" in response.json()["detail"]
class TestResponseStructure:
"""응답 구조 테스트 클래스"""
def test_index_response_type(self):
"""인덱스 응답이 JSON 형식인지 확인"""
response = client.get("/")
assert response.headers["content-type"] == "application/json"
def test_article_response_structure(self):
"""아티클 응답 구조 확인"""
response = client.get("/articles/100/")
data = response.json()
# 필수 필드 확인
required_fields = ["id", "title", "content"]
for field in required_fields:
assert field in data
# 데이터 타입 확인
assert isinstance(data["id"], int)
assert isinstance(data["title"], str)
assert isinstance(data["content"], str)
def test_post_response_structure(self):
"""포스트 응답 구조 확인"""
response = client.get("/posts/42/")
data = response.json()
# 필수 필드 확인
required_fields = ["id", "title", "content"]
for field in required_fields:
assert field in data
# 데이터 타입 확인
assert isinstance(data["id"], int)
assert isinstance(data["title"], str)
assert isinstance(data["content"], str)
@pytest.mark.parametrize("post_id,expected_title", [
(1, "포스팅 #1"),
(50, "포스팅 #50"),
(100, "포스팅 #100"),
])
def test_post_detail_parametrized(post_id, expected_title):
"""파라미터화된 포스트 상세 테스트 - 유효한 범위"""
response = client.get(f"/posts/{post_id}/")
assert response.status_code == 200
assert response.json()["title"] == expected_title
@pytest.mark.parametrize("post_id", [0, -1, 101, 200, 999, -100])
def test_post_detail_invalid_range(post_id):
"""파라미터화된 포스트 상세 테스트 - 무효한 범위"""
response = client.get(f"/posts/{post_id}/")
assert response.status_code == 404
assert "1 이상 100 이하" in response.json()["detail"]
# 실행 방법:
# pytest test_entry.py
# pytest test_entry.py -v # 상세 출력
# pytest test_entry.py::test_index # 특정 테스트만 실행
# pytest test_entry.py -k "post" # 이름에 "post"가 포함된 테스트만 실행
실행 방법
생성 요청 + 입력값 유효성 검사
from pydantic import BaseModel
@app.post("/posts/new/")
def post_new(post: PostCreate): # TODO: json 만 지원? form data 는?
# TODO: 호출 전에 Item을 통한 유효성 검사 끝.
return {"item": item}