测试
前言
FastAPI 基于 Starlette,可以直接使用 TestClient(底层是 httpx)对接口做冒烟测试,配合 pytest 组织测试用例。本章整理最常用的测试写法。
安装依赖:
python -m pip install pytest httpx
基础测试
假设应用在 main.py:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
@app.get("/ping")
async def ping():
return {"msg": "pong"}
@app.post("/items/")
async def create_item(item: Item):
return {"name": item.name, "price": item.price}
测试文件 test_main.py:
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_ping():
resp = client.get("/ping")
assert resp.status_code == 200
assert resp.json() == {"msg": "pong"}
def test_create_item():
resp = client.post("/items/", json={"name": "apple", "price": 3.5})
assert resp.status_code == 200
assert resp.json() == {"name": "apple", "price": 3.5}
def test_validation_error():
resp = client.post("/items/", json={"name": "apple"}) # 缺 price
assert resp.status_code == 422
运行:
python -m pytest test_main.py -v
依赖覆盖:dependency_overrides
测试中经常需要替换真实依赖(如数据库、外部 API)。FastAPI 提供了 app.dependency_overrides:
from fastapi import Depends, FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
def get_current_user():
return "real-user"
@app.get("/me")
def me(user: str = Depends(get_current_user)):
return {"user": user}
# 测试时覆盖依赖
def fake_get_current_user():
return "fake-user"
app.dependency_overrides[get_current_user] = fake_get_current_user
client = TestClient(app)
def test_me():
resp = client.get("/me")
assert resp.json() == {"user": "fake-user"}
# 用完清理
app.dependency_overrides.clear()
测试异常处理器与参数校验
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from fastapi.testclient import TestClient
app = FastAPI()
@app.exception_handler(RequestValidationError)
async def validation_handler(request, exc):
return JSONResponse(status_code=422, content={"msg": "参数错误", "errors": exc.errors()})
@app.get("/item")
def item(uid: int):
return {"uid": uid}
client = TestClient(app)
def test_validation_handler():
resp = client.get("/item?uid=abc")
assert resp.status_code == 422
assert resp.json()["msg"] == "参数错误"
按模块组织测试
推荐结构:
project/
├── app/
│ ├── main.py
│ └── routes/
└── tests/
├── conftest.py # 共享 fixture,如 TestClient
├── test_routes.py
└── test_auth.py
conftest.py:
import pytest
from fastapi.testclient import TestClient
from app.main import app
@pytest.fixture
def client():
with TestClient(app) as c:
yield c
提示
TestClient 作为上下文管理器使用时,会触发应用的 lifespan(启动/关闭钩子),适合测试需要连接数据库等资源的应用。
覆盖范围建议
- 每个路由至少一个成功用例和一个校验失败用例。
- 认证鉴权:未带 token、token 错误、token 过期。
- 自定义异常处理器返回的响应格式。
- 后台任务、中间件行为(如耗时头、CORS 头)。
- WebSocket 用
client.websocket_connect(...),见 WebSocket。