跳到主要内容

测试

前言

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