测试与调试
FastAPI 的测试栈:pytest + TestClient + dependency_overrides。面试主线:测试怎么起、依赖怎么替换、覆盖率怎么看。
TestClient
from fastapi.testclient import TestClient
def test_get_user():
with TestClient(app) as client: # with: 触发 lifespan
resp = client.get("/users/1")
assert resp.status_code == 200
assert resp.json()["name"] == "alice"- TestClient 基于 httpx,同步调用接口,不用起服务
with块触发应用生命周期(lifespan 启动/关闭),测数据库集成必须用- 断言状态码、响应体、响应头
pytest 组织
# conftest.py
import pytest
from fastapi.testclient import TestClient
@pytest.fixture
def client():
with TestClient(app) as c:
yield c
# test_users.py
def test_create_user(client):
resp = client.post("/users", json={"name": "alice", "email": "a@b.c"})
assert resp.status_code == 201conftest.py放共享 fixture,pytest 自动发现- 测试函数命名
test_前缀,文件test_*.py - fixture 的 yield 形式管理资源(测试后清理)
依赖覆盖:测数据库
def override_get_db():
yield MemoryDB() # 或测试库会话
app.dependency_overrides[get_db] = override_get_db
def test_list_users(client):
resp = client.get("/users")
assert resp.status_code == 200- 把真实数据库依赖换成内存/测试库,业务代码零改动
- 同理可替换:外部 API 客户端(mock)、鉴权依赖(固定用户)、发邮件
- 测试完
clear()恢复,或每个测试重新设置
覆盖率与调试
pytest --cov=app --cov-report=term-missing # 覆盖率报告- 覆盖率是手段不是目标:核心业务逻辑(鉴权、金额计算、状态流转)必须覆盖,模板和配置别凑数
- 调试手段:
print/日志、pytest -s(不捕获输出)、IDE 断点、--reload开发热重载 - 慢查询排查:日志里打 SQL 执行时间;生产用 APM
测试金字塔:单元测试(函数逻辑)为主,接口测试(TestClient)覆盖关键路径,端到端测试少量。FastAPI 的依赖覆盖让接口测试成本很低,多写。
面试追问
- TestClient 怎么用? httpx 基的同步客户端,with 块触发 lifespan。不用起真实服务
- 怎么测数据库相关接口? dependency_overrides 把 get_db 换成测试库/内存实现。业务代码零改动
- fixture 是什么? pytest 的资源管理:conftest.py 共享,yield 形式清理。测试间复用
- 覆盖率多少合适? 核心逻辑重点覆盖,别追数字。鉴权、金额、状态机必测
- 异步代码怎么测? TestClient 内部处理事件循环;纯异步函数用 pytest-asyncio 或 asyncio.run