用 pytest 给 FastAPI 写最小集成测试:覆盖成功、校验与错误响应

举报
yd_297598480 发表于 2026/11/07 00:59:29 2026/11/07
【摘要】 用 FastAPI TestClient 编写进程内接口测试,逐项检查正常响应、Pydantic 参数校验、业务错误以及测试与生产依赖隔离的做法。

接口能启动,只能说明应用进程起来了;接口能返回一次成功响应,也不代表参数校验和错误处理可靠。把 HTTP 请求写成自动化测试,可以在改动后快速检查状态码、响应结构和边界行为。下面用 FastAPI 自带的 TestClient 演示一组不需要启动服务器的进程内测试。

1. 安装测试依赖

在项目使用的虚拟环境中安装:

pip install fastapi pytest httpx

FastAPI 的 TestClient 通过 ASGI 应用直接发请求,不需要先运行 uvicorn,也不需要云资源。它适合检查路由、请求校验和响应序列化;真实网络、反向代理和部署配置仍需另行验证。

2. 准备一个小接口

例如在 app/main.py 中定义创建商品的接口:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI()

class ItemCreate(BaseModel):
    name: str
    price: float

@app.post("/items", status_code=201)
def create_item(item: ItemCreate):
    if item.price < 0:
        raise HTTPException(status_code=400, detail="price must be non-negative")
    return {"id": 1, **item.model_dump()}

@app.get("/health")
def health():
    return {"status": "ok"}

示例把存储省略了,重点是把三种结果区分开:成功创建、请求字段不符合模型、业务规则拒绝请求。实际项目可把业务规则放到服务层,路由只负责接收与返回。

3. 用 TestClient 覆盖三种结果

在项目根目录创建 tests/test_items.py:

from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_health_returns_ok():
    response = client.get("/health")

    assert response.status_code == 200
    assert response.json() == {"status": "ok"}

def test_create_item_returns_created_item():
    response = client.post(
        "/items",
        json={"name": "keyboard", "price": 199.0},
    )

    assert response.status_code == 201
    assert response.json() == {
        "id": 1,
        "name": "keyboard",
        "price": 199.0,
    }

def test_missing_price_is_rejected_by_validation():
    response = client.post("/items", json={"name": "keyboard"})

    assert response.status_code == 422
    assert response.json()["detail"]

def test_negative_price_returns_business_error():
    response = client.post(
        "/items",
        json={"name": "keyboard", "price": -1},
    )

    assert response.status_code == 400
    assert response.json()["detail"] == "price must be non-negative"

这几项断言各自保护不同契约。健康检查确认基础路由和 JSON 响应;成功用例检查创建状态码及字段;缺少价格时返回 422,说明请求模型校验生效;负数价格返回 400,说明业务错误没有被误当成成功。不要只断言“状态码不是 500”,那样会漏掉响应结构或错误类型变化。

4. 运行并读懂结果

在项目根目录执行:

pytest -q

测试通过后,pytest 会显示通过数量;失败时先看具体断言,再检查实际状态码和响应 JSON。若导入时报错,确认当前目录是项目根目录、包路径正确,并且运行 pytest 的 Python 与安装依赖的虚拟环境一致。

5. 有数据库或外部服务时隔离副作用

测试不要连生产数据库,也不要真的发短信、邮件或扣款。常见做法是把数据库依赖替换成测试实现、为每个用例准备独立数据,并在结束时回滚或清理;外部 HTTP 服务则用 mock 固定响应。这样测试结果可重复,也不会让一次本地验证改动真实业务数据。

TestClient 能验证应用层的 HTTP 行为,但不能替代部署后的端到端检查。发布前仍应在目标环境确认进程监听地址、代理转发、环境变量和依赖服务连接。把这两层检查分开,能更快判断问题是在代码契约还是运行环境。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。