43. Testing
1. Testing
- Starlette 덕분에 FastAPI 애플리케이션 테스트가 쉬워졌다.
- Requests 기반이므로 매우 직관적이다.
- 이를 통해 FastAPI로 pytest를 직접 사용할 수 있다.
2. Using TestClient
TestClient
를 임포트한다.- FastAPI에 전달하는
TestClient
를 생성한다. test_
로 시작하는 이름으로 함수를 만드는데, 이것은 표준pytest
규칙이다.requests
와 동일한 방식으로TestClient
객체를 사용한다.- 표준 Python 표현식으로 간단한
assert
문을 작성한다.
from fastapi import FastAPI, responses
from fastapi.testclient import TestClient
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
Note
- 테스트 함수는
async def
가 아닌 일반적인def
이다. 그리고 클라이언트에 대한 호출도await
을 사용하지 않는 일반적인 호출이다.
3. Separating tests
- 실제 애플리케이션에서는 다른 파일에 테스트할 것이 있을 수 있다.
- 그리고 FastAPI 애플리케이션은 여러 파일/모듈 등으로 구성될 수도 있다.
1) FastAPI app file
- FastAPI
app
이 있는main.py
파일이 있다고 가정해 보자.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
2) Testing file
- 그런 다음 테스트로
test_main.py
파일을 만들고 기본 모듈인main.py
에서app
을 가져올 수 있다.
from fastapi.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
4. Testing: extended example
- 위의 예제에 세부 사항을 추가하여, 다른 부분을 테스트하는 방법을 살펴보자.
1) Extended FastAPI app file
- FastAPI
app
이 있는main_b.py
파일이 있다고 가정해 보자. - 그리고 이 파일에는 에러를 반환할 수 있는
GET
operation이 있다. - 또한 여러 에러를 반환할 수 있는
POST
operation이 있다. - 이 두 path operations 모두
X-Token
header가 필요하다.
from typing import Optional
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
app = FastAPI()
fake_secret_token = "coneofsilence"
fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}
class Item(BaseModel):
id: str
title: str
description: Optional[str] = None
@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: str = Header(...)):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]
@app.post("/items/", response_model=Item)
async def create_item(item: Item, x_token: str = Header(...)):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=400, detail="Item already exists")
fake_db[item.id] = item
return item
2) Extended testing file
- 그런 다음 확장 테스트를 통해 이전과 동일한
test_main_b.py
를 가질 수 있다.
from fastapi.testclient import TestClient
from .main_b import app
client = TestClient(app)
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}
def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_read_inexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}
def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}
def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}
def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)
assert response.status_code == 400
assert response.json() == {"detail": "Item already exists"}
5. Run it
pytest
를 다음과 같이 설치한다.
pytest
는 파일과 테스트를 자동으로 감지하여 실행하고, 결과를 다시 보고한다.
- 다음과 같이 테스트를 실시할 수 있다.