用途
pytest-suite 用于在 vstorm-co/full-stack-ai-agent-template 项目中编写或扩展后端测试套件,使测试遵循该项目自身的约定。适用场景包括:为新的 service/route/repository 添加测试,或在覆盖率不足时补充测试。
测试结构
后端测试基于 pytest + anyio。测试文件放在 backend/tests/ 目录,并镜像源代码的目录结构,例如 app/services/user.py 对应 tests/services/test_user.py。测试运行时不需要真实数据库,conftest.py 通过 FastAPI 的 dependency_overrides 用 AsyncMock 覆盖 get_db_session。
关键 fixture(tests/conftest.py)
- client — 基于 httpx.AsyncClient 与 ASGITransport(app=app)(应使用此项,而非 Starlette TestClient)
- mock_db_session — 替代 AsyncSession 的 AsyncMock
- mock_redis(当启用 Redis 时)
- api_key_headers(当启用 API key 时)
编写模式
异步测试使用 anyio,而非 @pytest.mark.asyncio。可在模块顶部写 pytestmark = pytest.mark.anyio,也可对单个测试标记。
Service 测试:对 repo/session 的返回进行打桩,断言行为与异常。示例:
async def test_get_user_not_found_raises(monkeypatch, mock_db_session):
service = UserService(mock_db_session)
monkeypatch.setattr(user_repo, "get_by_id", AsyncMock(return_value=None))
with pytest.raises(NotFoundError):
await service.get_by_id(UUID("00000000-0000-0000-0000-000000000000"))
API 测试:通过 client 驱动路由,用 mock user 覆盖认证依赖。示例:
async def test_create_user_returns_201(client: AsyncClient):
app.dependency_overrides[get_current_user] = lambda: mock_user
resp = await client.post("/api/v1/users", json={"email": "a@b.com", "password": "secret123"})
assert resp.status_code == 201
命名与规则
- 命名格式:test_<action><scenario><expected_result>,例如 test_create_user_with_duplicate_email_raises_already_exists_error
- 每个测试只验证一个行为;使用普通 assert(pytest 会重写它)
- 使用工厂 fixture 生成数据,而不是原始 dict;每个测试相互独立
- 测试后清理 app.dependency_overrides(client fixture 已对 DB/Redis 覆盖做了清理)
运行方式
cd backend && uv run pytest # 全部测试
uv run pytest tests/services/test_user.py -v
uv run pytest --cov=app # 覆盖率
编写测试后,运行 uv run ruff check . --fix && uv run ruff format .,并确认 uv run pytest 全部通过。
限制
资料未提供该技能支持的测试框架以外的语言或平台,也未说明其是否适用于其他项目模板。