一个 PR 中的数十个后端测试:我们的 FastAPI 测试策略
为什么在一个 PR 中
在已有代码上添加测试是一个容易被推迟的任务——"我们稍后会补测试"。将其作为一个专注的单一 PR 来处理,迫使进行系统化而不是增量式的处理。它还将测试范围讨论集中在一个地方,而不是分散在数十个功能 PR 中。
Fixture 策略
测试 fixture 设计是决定测试套件规模是否可维护的关键。我们使用分层 fixture:
@pytest.fixture(scope="session")
async def db():
"""创建一个每个测试会话使用一次的测试数据库"""
engine = create_async_engine(TEST_DATABASE_URL)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield engine
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@pytest.fixture
async def session(db):
"""每个测试一个数据库会话,在事务中包装"""
async with AsyncSession(db) as session:
async with session.begin():
yield session
await session.rollback() # 每次测试后自动回滚关键点:数据库在会话级别创建一次,但每个测试在事务中运行,测试后自动回滚。这使得测试相互隔离,同时避免了重复的数据库创建开销。
认证模拟
FastAPI 的依赖注入系统使认证模拟变得简单:
def override_get_current_user():
return User(id="test-user", role="admin")
app.dependency_overrides[get_current_user] = override_get_current_user这避免了实际 JWT 验证,而不需要模拟任何底层的认证基础设施。
测试覆盖率优先级
100% 的测试覆盖率通常不是一个有价值的目标——它优化了一个表面指标,而不是实际的测试质量。我们优先覆盖:业务逻辑(数据转换、计算)、认证和授权边界,以及已知出现过错误的代码路径。工具代码(日志记录、配置加载)覆盖优先级较低。