确保质量: 测试
确保质量:测试
Section titled “确保质量:测试”可靠性是专业软件工程的基石。即使最快的 API,如果它返回不正确的数据或意外崩溃,也毫无用处。在本章中,我们将探讨如何为你的 FastAPI 应用程序编写自动化测试,确保你的代码在投入生产之前完全符合预期。
工具:Pytest 和 TestClient
Section titled “工具:Pytest 和 TestClient”FastAPI 站在巨人的肩膀上,在测试方面,它利用了 Starlette 的强大功能。我们将使用 TestClient,这是一个允许你模拟对应用程序进行 HTTP 请求而无需实际运行服务器的工具。它直接与你的 FastAPI 应用程序对象进行交互。
对于测试运行器,Python 生态系统中的行业标准是 pytest。它使用简单但功能强大。
首先,你需要安装 pytest 和 httpx。TestClient 在底层使用 httpx 来执行请求。
pip install pytest httpx编写你的第一个测试
Section titled “编写你的第一个测试”假设你有一个简单的 main.py 文件。
from fastapi import FastAPI
app = FastAPI()
@app.get("/")def read_root(): return {"msg": "Hello World"}为了测试它,创建一个名为 test_main.py 的文件。在这个文件中,我们导入 TestClient 和我们的 app。
from fastapi.testclient import TestClientfrom .main import app
client = TestClient(app)
def test_read_root(): response = client.get("/") assert response.status_code == 200 assert response.json() == {"msg": "Hello World"}要运行测试,只需在终端中执行 pytest。pytest 会自动检测以 test_ 开头的文件和以 test_ 开头的函数。
测试错误情况
Section titled “测试错误情况”测试不仅仅是关于“成功路径”(一切顺利的情况)。同样重要的是要确保当给定无效输入时,你的 API 能够优雅地失败。由于 FastAPI 使用 Pydantic 自动验证数据,我们可以测试这些验证错误是否按预期发生。
@app.get("/items/{item_id}")def read_item(item_id: int): return {"item_id": item_id}
# test_main.pydef test_read_item_bad_type(): # We send a string "foo", but the API expects an integer # 我们发送字符串 "foo",但 API 期望一个整数 response = client.get("/items/foo") assert response.status_code == 422 # We can even check the specific error message structure # 我们甚至可以检查特定的错误消息结构 assert response.json()["detail"][0]["msg"] == "Input should be a valid integer"隔离环境:覆盖依赖
Section titled “隔离环境:覆盖依赖”FastAPI 测试系统最强大的功能之一是能够覆盖依赖项(override dependencies)。当你的应用程序与外部资源(如数据库、身份验证提供程序或电子邮件服务)交互时,这一点至关重要。
你通常不希望你的测试修改生产数据库或发送真实的电子邮件。FastAPI 允许你使用 app.dependency_overrides 将真实的依赖项替换为“模拟”(mock)或测试版本。
假设你有一个验证 token 的依赖项:
# main.py dependencyasync def verify_token(x_token: str = Header(...)): if x_token != "super-secret-token": raise HTTPException(status_code=400, detail="Invalid X-Token header") return x_token在你的测试中,你可以通过覆盖它来绕过检查特定字符串的逻辑。
from fastapi.testclient import TestClientfrom .main import app, verify_token
client = TestClient(app)
async def override_verify_token(): # We simply return a value, bypassing the check # 我们只是简单地返回一个值,绕过了检查 return "test-token"
# Apply the override# 应用覆盖app.dependency_overrides[verify_token] = override_verify_token
def test_secure_endpoint(): response = client.get("/secure-data") assert response.status_code == 200
# Clean up the override after the test so other tests aren't affected# 测试后清除覆盖,以免影响其他测试app.dependency_overrides = {}这项技术被广泛用于将真实的 SQL 数据库会话替换为临时的、内存中的 SQLite 数据库进行测试,从而确保你的测试快速、隔离且安全。