Skip to content

自动化测试 ​

测试关注接口行为 ​

手动操作文档适合探索接口;自动化测试适合在修改代码后确认已有约定仍然成立。

好的测试不仅看成功,还看不存在的资源、错误输入、局部更新、权限不足和响应字段。无需为了凑数量逐行重复实现逻辑。

安装:

powershell
python -m pip install pytest httpx2

TestClient 可以直接测试应用,不必启动独立监听端口的服务器,普通调用使用同步方法即可。官方测试说明

版本说明:当前 Starlette 的测试客户端推荐安装 httpx2。旧教程常写 httpx,在部分新版环境中仍可运行,但会出现弃用提示。本手册的异步网络请求示例仍使用 httpx,测试示例使用 TestClient,二者的导入代码不同。Starlette 官方测试客户端说明

完整示例:测试第 14 章 ​

将第 14 章代码保存为 todo_memory.py,在同一目录保存 test_todo_memory.py:

python
from itertools import count

import pytest
from fastapi.testclient import TestClient
import todo_memory


@pytest.fixture
def client():
    todo_memory.todos.clear()
    todo_memory.id_counter = count(1)
    with TestClient(todo_memory.app) as test_client:
        yield test_client
    todo_memory.todos.clear()


def test_create_and_read(client):
    response = client.post("/todos", json={"title": "学习测试"})
    assert response.status_code == 201
    todo_id = response.json()["id"]
    detail = client.get(f"/todos/{todo_id}")
    assert detail.status_code == 200
    assert detail.json()["title"] == "学习测试"
    assert detail.json()["done"] is False


@pytest.mark.parametrize("payload", [
    {"title": ""},
    {"title": "   "},
    {"title": "学习", "unknown_field": 1},
])
def test_invalid_create(client, payload):
    assert client.post("/todos", json=payload).status_code == 422


def test_patch_preserves_and_clears_fields(client):
    created = client.post(
        "/todos", json={"title": "原始标题", "description": "原始描述"}
    ).json()
    path = f"/todos/{created['id']}"
    updated = client.patch(path, json={"done": True}).json()
    assert updated["title"] == "原始标题"
    assert updated["description"] == "原始描述"
    assert updated["done"] is True
    cleared = client.patch(path, json={"description": None}).json()
    assert cleared["description"] is None
    assert client.patch(path, json={"title": None}).status_code == 422


def test_delete_and_missing(client):
    created = client.post("/todos", json={"title": "待删除"}).json()
    path = f"/todos/{created['id']}"
    response = client.delete(path)
    assert response.status_code == 204
    assert response.content == b""
    assert client.get(path).status_code == 404


def test_invalid_pagination(client):
    assert client.get("/todos?limit=0").status_code == 422
    assert client.get("/todos?skip=-1").status_code == 422

运行:

powershell
python -m pytest test_todo_memory.py -q

json= 表示把 Python 数据编码为 JSON 请求体;response.json() 解析 JSON 响应。fixture 为每个测试准备干净数据,避免测试相互影响。

数据库测试:改用独立 MySQL 8.0 测试库 ​

第 15 章使用 MySQL 8.0,第 26~38 章使用 Tortoise ORM。对应的完整数据库测试放在第 40 章,包含隔离测试库配置、异步客户端、fixture、增删改查、用户归属、空值更新、多对多关系、唯一约束、事务回滚与并发扣库存。

前面的内存接口测试仍可独立运行。学习数据库后,连接专用 MySQL 8.0 测试库验证实际约束与事务;测试账号只获得测试库权限。不要用正式库运行测试。

第 40 章的执行方式:

powershell
python -m pip install pytest pytest-asyncio httpx2
python -m pytest tests/test_mysql_todos.py -q

HTTPX 的 ASGI transport 不会自动执行 lifespan,异步测试中需显式进入应用 lifespan;数据库连接与使用它的测试操作保持在正确的事件循环中。具体代码见第 40 章。

lifespan 测试要使用上下文 ​

使用 with TestClient(app) as client: 会让测试进入应用生命周期。仅创建一个客户端对象,不应被当作已经完整执行 startup 和 shutdown 的证据。官方生命周期测试说明

推荐测试矩阵 ​

功能至少验证什么
创建成功、必填字段缺失、非法值
查询存在、不存在、分页范围、筛选
PATCH保留未传字段、清空可空字段、拒绝非法空值
删除无正文、删除后不存在
登录正确密码、错误密码、未知用户
Token缺少、篡改、过期、缺少声明
授权普通用户不能执行管理员操作、不能访问他人数据
文件小文件成功、超限拒绝

本章练习 ​

为第 16 章补上认证测试;为内存项目补上列表筛选、空 PATCH 和 PUT 替换测试。

过关标准:能设计独立、可重复运行的测试,且测试能发现真正的行为变化。