穿越来找茬
35.39MB · 2025-09-13
需求驱动测试(Requirement-Driven Testing)是在测试驱动开发(TDD)中先根据需求定义测试用例,再实现功能的开发方法。在FastAPI开发中,这意味着:
这能确保代码精确满足需求且具备可测性。
graph TD
A[分析API需求] --> B[定义输入/输出]
B --> C[编写Pydantic模型]
C --> D[创建测试断言]
D --> E[实现业务逻辑]
E --> F[重构优化]
在FastAPI中需要覆盖:
假设需求文档要求:
# test_user_api.py
# 所需依赖:pytest==7.1.2, httpx==0.23.0
from fastapi.testclient import TestClient
from pydantic import BaseModel
import pytest
class UserCreate(BaseModel):
username: str
password: str
# 测试类封装
class TestUserRegistration:
@pytest.fixture(autouse=True)
def setup(self, client: TestClient):
self.client = client
self.url = "/register"
def test_successful_registration(self):
"""需求1&4:验证成功注册"""
response = self.client.post(
self.url,
json={"username": "new_user", "password": "StrongPass123"}
)
assert response.status_code == 201
assert "user_id" in response.json()
def test_username_conflict(self):
"""需求3:验证用户名冲突"""
# 先创建测试用户
self.client.post(self.url, json={
"username": "existing",
"password": "Password123"
})
# 再次使用相同用户名
response = self.client.post(
self.url,
json={"username": "existing", "password": "NewPass456"}
)
assert response.status_code == 409
assert response.json()["detail"] == "Username already exists"
def test_invalid_password(self):
"""需求2:验证密码规则"""
response = self.client.post(
self.url,
json={"username": "short_pass", "password": "abc"}
)
assert response.status_code == 422
errors = response.json()["detail"]
assert any("ensure this value has at least 8 characters" in e["msg"] for e in errors)
# main.py
# 所需依赖:fastapi==0.78.0, pydantic==1.10.2
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, constr
app = FastAPI()
mock_db = []
# 使用Pydantic严格数据约束
class UserCreate(BaseModel):
username: constr(min_length=5, max_length=20)
password: constr(min_length=8)
@app.post("/register", status_code=status.HTTP_201_CREATED)
def register_user(user: UserCreate):
"""实现用户注册逻辑"""
# 检查用户名冲突
if any(u["username"] == user.username for u in mock_db):
raise HTTPException(
status_code=409,
detail="Username already exists"
)
# 创建用户记录(模拟DB插入)
new_user = {
"user_id": len(mock_db) + 1,
"username": user.username
}
mock_db.append(new_user)
return new_user
constr
限制字段长度,自动返回422错误status
模块保证状态码常量正确性当测试返回422错误时,通常表示什么类型的问题?
A) 服务器内部错误
B) 权限验证失败
C) 请求数据验证失败
D) 路由不存在
在需求驱动测试中,应该何时编写业务逻辑代码?
A) 在编写测试用例之前
B) 与测试用例同时编写
C) 在测试用例失败之后
D) 在所有测试设计完成后
如何处理API的多版本兼容测试需求?
A) 为每个版本复制测试套件
B) 使用参数化测试覆盖不同版本
C) 忽略老版本测试
D) 在路由中使用版本前缀
# 参考答案B示例
@pytest.mark.parametrize("version", ["v1", "v2"])
def test_api_version_compatibility(client, version):
response = client.get(f"/{version}/users")
assert response.status_code == 200
触发场景:
{
"detail": [
{
"loc": ["body", "password"],
"msg": "ensure this value has at least 8 characters",
"type": "value_error.any_str.min_length"
}
]
}
解决方案:
password: constr(
min_length=8,
error_msg="密码长度至少8个字符"
)
触发场景: 向未定义的路由发送请求时
解决方案:
排查步骤:
@app.middleware("http")
async def catch_exceptions(request, call_next):
try:
return await call_next(request)
except Exception as exc:
logger.error(f"Unhandled exception: {exc}")
return JSONResponse(...)