16 KiB
16 KiB
测试最佳实践
**本文档引用的文件** - [tests/conftest.py](file://tests/conftest.py) - [tests/test_auth.py](file://tests/test_auth.py) - [tests/test_citations.py](file://tests/test_citations.py) - [tests/test_queries.py](file://tests/test_queries.py) - [tests/test_citation_engine.py](file://tests/test_citation_engine.py) - [backend/requirements.txt](file://backend/requirements.txt) - [docker-compose.yml](file://docker-compose.yml) - [backend/app/main.py](file://backend/app/main.py) - [backend/app/api/deps.py](file://backend/app/api/deps.py) - [backend/app/workers/citation_engine.py](file://backend/app/workers/citation_engine.py) - [backend/app/workers/scheduler.py](file://backend/app/workers/scheduler.py) - [backend/alembic.ini](file://backend/alembic.ini)目录
简介
本指南面向GEO项目的测试工作,系统性地给出测试编写规范、覆盖率要求与测量方法、持续集成配置建议、调试技巧以及测试环境与数据管理的最佳实践。文档以仓库现有测试代码为基础,结合后端FastAPI应用、数据库与任务调度器的实际实现,帮助团队建立一致、可维护、高覆盖度的测试体系。
项目结构
测试相关的核心位置与职责如下:
- tests 目录:集中存放pytest测试用例与通用fixture配置
- backend/app:后端应用入口、API路由、依赖注入、模型与服务
- backend/app/workers:引用检测引擎与任务调度器
- docker-compose.yml:本地开发与测试所需的数据库与缓存服务编排
graph TB
subgraph "测试层"
T1["tests/conftest.py"]
T2["tests/test_auth.py"]
T3["tests/test_citations.py"]
T4["tests/test_queries.py"]
T5["tests/test_citation_engine.py"]
end
subgraph "后端应用"
A1["app/main.py"]
A2["app/api/deps.py"]
W1["workers/citation_engine.py"]
W2["workers/scheduler.py"]
end
subgraph "基础设施"
D1["docker-compose.yml"]
DB["PostgreSQL"]
R["Redis"]
end
T1 --> A1
T2 --> A1
T3 --> A1
T4 --> A1
T5 --> W1
A1 --> W2
W1 --> DB
W2 --> DB
D1 --> DB
D1 --> R
图表来源
- tests/conftest.py:1-71
- tests/test_auth.py:1-104
- tests/test_citations.py:1-93
- tests/test_queries.py:1-154
- tests/test_citation_engine.py:1-54
- backend/app/main.py:1-48
- backend/app/api/deps.py:1-43
- backend/app/workers/citation_engine.py:1-309
- backend/app/workers/scheduler.py:1-95
- docker-compose.yml:1-71
章节来源
核心组件
- 测试夹具与环境
- 会话级mock:屏蔽真实任务调度器,避免后台作业影响测试稳定性
- 认证夹具:生成模拟用户、JWT令牌与请求头,统一处理鉴权依赖
- 异步HTTP客户端:基于ASGI传输,便于对FastAPI路由进行端到端测试
- API测试范围
- 认证模块:注册、登录、个人信息读取
- 查询模块:创建、列表、更新、删除、详情与权限边界
- 引用数据模块:查询、统计、导出CSV
- 单元测试范围
- 引用检测引擎:品牌匹配、竞争品牌识别、引用位置与置信度
- 测试依赖
- pytest、pytest-asyncio、httpx、aiosqlite、unittest.mock
章节来源
- tests/conftest.py:19-71
- tests/test_auth.py:25-104
- tests/test_queries.py:29-154
- tests/test_citations.py:23-93
- tests/test_citation_engine.py:6-54
- backend/requirements.txt:31-35
架构总览
下图展示测试执行路径与关键依赖交互,体现测试如何通过夹具注入、路由访问与服务mock实现可控的测试场景。
sequenceDiagram
participant Py as "pytest"
participant Fix as "conftest夹具"
participant AC as "AsyncClient"
participant APP as "FastAPI应用"
participant DEP as "依赖注入(get_current_user)"
participant SVC as "业务服务(被patch)"
participant DB as "数据库/缓存"
Py->>Fix : "加载会话级fixture"
Fix->>APP : "注入依赖覆盖(get_current_user)"
Py->>AC : "创建异步HTTP客户端"
AC->>APP : "发送API请求"
APP->>DEP : "解析JWT并解析当前用户"
DEP-->>APP : "返回模拟用户"
APP->>SVC : "调用业务逻辑(被patch)"
SVC-->>APP : "返回测试数据/异常"
APP-->>AC : "响应JSON/状态码"
AC-->>Py : "断言结果"
APP->>DB : "读写(测试中通常隔离)"
图表来源
- tests/conftest.py:19-71
- tests/test_auth.py:25-104
- tests/test_queries.py:29-154
- tests/test_citations.py:23-93
- backend/app/api/deps.py:16-43
- backend/app/main.py:13-48
详细组件分析
测试命名约定与结构
- 文件命名
- 使用 test_xxx.py,如 test_auth.py、test_queries.py、test_citations.py、test_citation_engine.py
- 函数命名
- 使用 test_xxx_pattern,如 test_register_success、test_create_query_exceeds_limit
- 夹具命名
- 使用 fixture_xxx 或 mock_xxx,如 mock_user、auth_headers、override_get_current_user
- 断言风格
- 明确断言状态码、响应体字段与业务语义,如“包含特定错误信息”“返回CSV头”
- 注释标准
- 每个fixture与关键测试函数添加简要说明,解释用途与边界条件
章节来源
- tests/test_auth.py:11-22
- tests/test_queries.py:10-26
- tests/test_citations.py:8-20
- tests/test_citation_engine.py:3-5
认证与授权测试
- 关键点
- 使用依赖覆盖模拟当前用户,确保受保护路由在已认证与未认证两种场景下的行为
- 通过patch替换服务层方法,验证成功与失败路径
- 推荐断言
- 成功:200/201响应、返回必要字段、token类型与用户信息
- 失败:4xx状态码、错误消息包含预期关键字
- 安全性
- 确保未覆盖依赖时,未认证访问返回401
sequenceDiagram
participant C as "客户端"
participant A as "认证路由"
participant D as "get_current_user"
participant S as "认证服务(被patch)"
C->>A : "POST /api/v1/auth/login"
A->>D : "解析JWT并解析用户"
D-->>A : "返回模拟用户"
A->>S : "authenticate_user(...)"
S-->>A : "返回用户或None"
A-->>C : "200/401 + JSON"
图表来源
章节来源
查询管理测试
- 关键点
- 使用mock对象构造典型查询实体,覆盖创建、列表、更新、删除、不存在与越权访问
- 通过patch触发权限限制与业务异常,验证403/404响应
- 推荐断言
- 字段一致性、分页总数、状态转换、权限边界
flowchart TD
Start(["开始: 发送请求"]) --> Patch["patch 业务方法"]
Patch --> CallAPI["调用 /queries/* 路由"]
CallAPI --> Resp{"响应状态码"}
Resp --> |201/200| AssertOK["断言返回字段与业务语义"]
Resp --> |403/404| AssertErr["断言错误消息与权限边界"]
AssertOK --> End(["结束"])
AssertErr --> End
图表来源
章节来源
引用数据与报告测试
- 关键点
- mock服务返回结构化数据,验证列表、统计聚合与CSV导出的响应头与内容
- 推荐断言
- 统计字段存在性与合理性、CSV内容片段包含关键字段
章节来源
引用检测引擎单元测试
- 关键点
- 测试品牌匹配器的精确、别名、模糊匹配与无匹配场景,以及引用位置与置信度
- 测试竞争品牌检测器对不同行业类别的识别
- 推荐断言
- 匹配结果布尔值、匹配类型、置信度范围、位置编号与上下文片段
classDiagram
class BrandMatcher {
+match(text) dict
-_extract_candidates(text) list
-_extract_position_and_context(text, keyword) tuple
}
class CompetitorDetector {
+detect(text, target_brand) list
}
BrandMatcher <.. CompetitorDetector : "组合使用"
图表来源
章节来源
依赖分析
- 测试与应用的耦合
- 通过依赖覆盖与patch解耦具体实现细节,提升测试稳定性
- 会话级mock调度器避免真实任务执行,保证测试幂等性
- 外部依赖
- PostgreSQL与Redis通过docker-compose提供,测试可选择独立数据库或内存数据库(如aiosqlite)以加速
- 日志与可观测性
- 引擎与调度器使用标准日志,便于定位问题;Alembic日志级别较低,避免噪声
graph LR
Py["pytest"] --> CF["conftest.py"]
CF --> APP["app/main.py"]
APP --> SCH["workers/scheduler.py"]
SCH --> CE["workers/citation_engine.py"]
CE --> DB["PostgreSQL/Redis"]
图表来源
- tests/conftest.py:19-25
- backend/app/main.py:10-21
- backend/app/workers/scheduler.py:25-95
- backend/app/workers/citation_engine.py:148-309
章节来源
- tests/conftest.py:19-25
- backend/app/main.py:10-21
- backend/app/workers/scheduler.py:25-95
- backend/app/workers/citation_engine.py:148-309
- backend/alembic.ini:115-149
性能考虑
- 测试速度
- 使用会话级fixture减少重复初始化成本
- 将外部依赖(数据库、缓存)置于容器中,避免每次重启
- 并发与异步
- 使用pytest-asyncio与AsyncClient,确保异步测试稳定
- 覆盖率
- 建议优先达到以下目标:行覆盖率≥80%、分支覆盖率≥70%、功能覆盖率≥90%
- 对关键路径(认证、查询、引用检测)进行重点覆盖
故障排查指南
- 常见问题
- 未覆盖依赖导致未认证访问失败:确认在测试中正确注入依赖覆盖
- patch目标不匹配:核对被patch的服务方法签名与调用路径
- 调度器干扰:确保会话级mock调度器生效
- 调试技巧
- 使用pytest调试选项:--pdb(进入调试器)、-v(详细输出)、-s(打印日志)
- 启用更详细的日志:在测试前设置日志级别,关注引擎与调度器日志
- 分离数据库:使用独立测试数据库或内存数据库(如aiosqlite)以避免数据污染
- 错误排查步骤
- 逐个缩小patch范围,确认业务方法是否被正确替换
- 检查JWT生成与解析流程,确保令牌有效
- 校验路由前缀与路径参数,避免404
章节来源
结论
通过统一的命名与结构规范、完善的夹具与mock策略、清晰的断言与覆盖率目标,以及合理的CI/CD与调试实践,GEO项目可以构建高质量、可维护的测试体系。建议在现有基础上逐步扩展覆盖率,并引入自动化CI流水线以保障质量。
附录
测试覆盖率要求与测量方法
- 行覆盖率(Line Coverage)
- 目标:≥80%
- 方法:使用覆盖率工具(如pytest-cov)生成报告,关注未覆盖的分支与异常路径
- 分支覆盖率(Branch Coverage)
- 目标:≥70%
- 方法:针对if/else、异常处理与权限判断路径分别设计用例
- 功能覆盖率(Functional Coverage)
- 目标:≥90%
- 方法:以API端点与核心算法(品牌匹配、竞争品牌检测)为功能域,确保每个功能域至少有一个正向与一个反向用例
持续集成配置建议
- 触发条件
- push到主分支与拉取请求
- 步骤建议
- 安装依赖(后端requirements.txt)
- 启动PostgreSQL与Redis(可复用docker-compose)
- 运行pytest并生成覆盖率报告
- 上传覆盖率与测试报告
- 参考文件
章节来源
测试环境管理
- 数据库
- 开发:使用docker-compose提供的PostgreSQL
- 测试:可选独立测试库或内存数据库(如aiosqlite),避免跨测试污染
- 缓存
- Redis用于任务调度与缓存,测试中可直接使用容器版本
- 依赖注入
- 通过conftest中的依赖覆盖,确保测试中始终使用mock用户与禁用真实调度器
章节来源
测试数据管理
- 建议
- 使用fixture生成稳定的UUID与时间戳,确保可重复性
- 对于复杂对象(查询、引用记录),在fixture中集中定义字段默认值
- 对于CSV导出等场景,使用固定字符串片段进行断言,避免动态内容导致脆弱断言
章节来源
测试报告生成
- 建议
- 使用pytest-cov生成HTML或XML报告,便于CI集成
- 在CI中保留报告产物,便于回溯历史趋势
- 日志配置
- Alembic日志级别已较低,可在测试中临时提高日志级别以辅助排查
章节来源