1. FastAPI 测试环境配置与基础框架搭建
1.1 测试环境配置要点
在 FastAPI 项目中配置测试环境需关注:
- 隔离性 :通过
TestClient
创建独立环境,避免污染生产数据 - 依赖管理 :使用
pytest
及其插件(如pytest-asyncio
)处理异步代码 - 环境变量 :通过
.env.test
文件加载测试专用配置
bash
# 安装核心测试依赖
pip install pytest==7.4.0 httpx==0.27.0 pytest-asyncio==0.23.0
1.2 测试客户端初始化
创建 tests/conftest.py
文件统一管理测试资源:
python
import pytest
from fastapi.testclient import TestClient
from app.main import app # 主应用入口
@pytest.fixture(scope="module")
def test_client():
"""创建全局共享的测试客户端"""
with TestClient(app) as client:
yield client # 测试用例中通过注入使用
关键点 :
scope="module"
确保单个测试模块内复用客户端
1.3 基础测试框架搭建
使用分层结构组织测试代码:
bash
my_project/
├── app/
│ ├── main.py # FastAPI 实例
│ └── routers/ # 路由模块
└── tests/
├── unit/ # 单元测试
├── integration/ # 集成测试
├── conftest.py # 全局测试配置
└── test_main.py # 应用入口测试
2. 工程化测试目录结构规范
2.1 分层测试策略
测试类型 | 测试范围 | 目录位置 |
---|---|---|
单元测试 | 独立函数/类 | tests/unit/ |
集成测试 | 模块间交互 | tests/integration/ |
E2E 测试 | 完整业务流程 | tests/e2e/ |
2.2 测试文件命名规范
- 模式 :
test_<模块名>_<功能>.py
- 示例 :
test_user_create.py
test_payment_process.py
- 禁用 :模糊命名如
test_utils.py
2.3 Fixture 管理规范
在 tests/conftest.py
中定义通用 Fixture:
python
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
@pytest.fixture
def db_session():
"""创建隔离的数据库会话"""
engine = create_engine("sqlite:///./test.db")
TestingSessionLocal = sessionmaker(autocommit=False, bind=engine)
db = TestingSessionLocal()
try:
yield db
finally:
db.close() # 确保测试后清理连接
3. 测试案例演示
3.1 测试用户注册接口
python
# tests/integration/test_user_reg.py
import pytest
from app.schemas import UserCreate # Pydantic 模型
def test_user_registration(test_client, db_session):
"""测试用户注册全流程"""
# 1. 准备测试数据
user_data = {
"email": "test@example.com",
"password": "SecurePass123!"
}
# 2. 调用接口
response = test_client.post("/users/", json=user_data)
# 3. 验证结果
assert response.status_code == 201
assert "id" in response.json()
# 4. 清理数据库
db_session.execute("DELETE FROM users WHERE email = :email", user_data)
3.2 使用 Pytest 参数化测试
python
@pytest.mark.parametrize("email, password, expected_code", [
("valid@email.com", "Str0ngPwd!", 201), # 合法数据
("invalid-email", "weak", 422), # 格式错误
("admin@test.com", "short", 422), # 密码强度不足
])
def test_registration_validation(test_client, email, password, expected_code):
response = test_client.post("/users/", json={"email": email, "password": password})
assert response.status_code == expected_code
课后 Quiz
❓ 问题 :在数据库交互测试中,如何避免 SQL 注入风险?
✅ 答案:
-
参数化查询 :使用 SQLAlchemy 的
text()
与命名参数python# 错误方式(漏洞) db.execute(f"SELECT * FROM users WHERE email = '{email}'") # 正确方式(防注入) from sqlalchemy import text db.execute(text("SELECT * FROM users WHERE email = :email"), {"email": email})
-
ORM 优先:优先使用 SQLAlchemy ORM 而非原生 SQL
-
输入验证:通过 Pydantic 模型校验传入参数
常见报错解决方案
报错 1:422 Unprocessable Entity
产生原因:
- 请求体不符合 Pydantic 模型定义
- 缺少必填字段或类型错误
修复步骤:
python
# 正确示例:使用模型类定义请求
@app.post("/users/")
def create_user(user: UserCreate): # 强类型校验
...
# 模型定义(强制约束)
class UserCreate(BaseModel):
email: EmailStr # 使用EmailStr进行格式验证
password: str = Field(min_length=8, regex=r"^(?=.*\d)(?=.*[A-Z]).+$")
报错 2:RuntimeError: Event loop is closed
产生原因:
- 异步代码测试未配置
pytest-asyncio
- 测试结束前未正确关闭资源
解决方案:
-
安装异步支持:
bashpip install pytest-asyncio==0.23.0
-
添加异步标记:
python@pytest.mark.asyncio async def test_async_endpoint(): response = await test_client.get("/async-route")
通过合理配置测试环境、规范目录结构、采用分层测试策略,可显著提升 FastAPI 项目的测试效率与代码质量。实践中应重点关注:
- 测试数据隔离(每次测试后自动清理)
- Pydantic 模型的边界值验证
- 持续集成(CI)中的测试流程编排