geo/.qoder/repowiki/zh/content/测试策略/测试最佳实践.md

384 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 测试最佳实践
<cite>
**本文档引用的文件**
- [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)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本指南面向GEO项目的测试工作系统性地给出测试编写规范、覆盖率要求与测量方法、持续集成配置建议、调试技巧以及测试环境与数据管理的最佳实践。文档以仓库现有测试代码为基础结合后端FastAPI应用、数据库与任务调度器的实际实现帮助团队建立一致、可维护、高覆盖度的测试体系。
## 项目结构
测试相关的核心位置与职责如下:
- tests 目录集中存放pytest测试用例与通用fixture配置
- backend/app后端应用入口、API路由、依赖注入、模型与服务
- backend/app/workers引用检测引擎与任务调度器
- docker-compose.yml本地开发与测试所需的数据库与缓存服务编排
```mermaid
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](file://tests/conftest.py#L1-L71)
- [tests/test_auth.py:1-104](file://tests/test_auth.py#L1-L104)
- [tests/test_citations.py:1-93](file://tests/test_citations.py#L1-L93)
- [tests/test_queries.py:1-154](file://tests/test_queries.py#L1-L154)
- [tests/test_citation_engine.py:1-54](file://tests/test_citation_engine.py#L1-L54)
- [backend/app/main.py:1-48](file://backend/app/main.py#L1-L48)
- [backend/app/api/deps.py:1-43](file://backend/app/api/deps.py#L1-L43)
- [backend/app/workers/citation_engine.py:1-309](file://backend/app/workers/citation_engine.py#L1-L309)
- [backend/app/workers/scheduler.py:1-95](file://backend/app/workers/scheduler.py#L1-L95)
- [docker-compose.yml:1-71](file://docker-compose.yml#L1-L71)
章节来源
- [tests/conftest.py:1-71](file://tests/conftest.py#L1-L71)
- [docker-compose.yml:1-71](file://docker-compose.yml#L1-L71)
## 核心组件
- 测试夹具与环境
- 会话级mock屏蔽真实任务调度器避免后台作业影响测试稳定性
- 认证夹具生成模拟用户、JWT令牌与请求头统一处理鉴权依赖
- 异步HTTP客户端基于ASGI传输便于对FastAPI路由进行端到端测试
- API测试范围
- 认证模块:注册、登录、个人信息读取
- 查询模块:创建、列表、更新、删除、详情与权限边界
- 引用数据模块查询、统计、导出CSV
- 单元测试范围
- 引用检测引擎:品牌匹配、竞争品牌识别、引用位置与置信度
- 测试依赖
- pytest、pytest-asyncio、httpx、aiosqlite、unittest.mock
章节来源
- [tests/conftest.py:19-71](file://tests/conftest.py#L19-L71)
- [tests/test_auth.py:25-104](file://tests/test_auth.py#L25-L104)
- [tests/test_queries.py:29-154](file://tests/test_queries.py#L29-L154)
- [tests/test_citations.py:23-93](file://tests/test_citations.py#L23-L93)
- [tests/test_citation_engine.py:6-54](file://tests/test_citation_engine.py#L6-L54)
- [backend/requirements.txt:31-35](file://backend/requirements.txt#L31-L35)
## 架构总览
下图展示测试执行路径与关键依赖交互体现测试如何通过夹具注入、路由访问与服务mock实现可控的测试场景。
```mermaid
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](file://tests/conftest.py#L19-L71)
- [tests/test_auth.py:25-104](file://tests/test_auth.py#L25-L104)
- [tests/test_queries.py:29-154](file://tests/test_queries.py#L29-L154)
- [tests/test_citations.py:23-93](file://tests/test_citations.py#L23-L93)
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
- [backend/app/main.py:13-48](file://backend/app/main.py#L13-L48)
## 详细组件分析
### 测试命名约定与结构
- 文件命名
- 使用 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](file://tests/test_auth.py#L11-L22)
- [tests/test_queries.py:10-26](file://tests/test_queries.py#L10-L26)
- [tests/test_citations.py:8-20](file://tests/test_citations.py#L8-L20)
- [tests/test_citation_engine.py:3-5](file://tests/test_citation_engine.py#L3-L5)
### 认证与授权测试
- 关键点
- 使用依赖覆盖模拟当前用户,确保受保护路由在已认证与未认证两种场景下的行为
- 通过patch替换服务层方法验证成功与失败路径
- 推荐断言
- 成功200/201响应、返回必要字段、token类型与用户信息
- 失败4xx状态码、错误消息包含预期关键字
- 安全性
- 确保未覆盖依赖时未认证访问返回401
```mermaid
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"
```
图表来源
- [tests/test_auth.py:62-84](file://tests/test_auth.py#L62-L84)
- [tests/conftest.py:42-50](file://tests/conftest.py#L42-L50)
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
章节来源
- [tests/test_auth.py:25-104](file://tests/test_auth.py#L25-L104)
- [tests/conftest.py:28-62](file://tests/conftest.py#L28-L62)
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
### 查询管理测试
- 关键点
- 使用mock对象构造典型查询实体覆盖创建、列表、更新、删除、不存在与越权访问
- 通过patch触发权限限制与业务异常验证403/404响应
- 推荐断言
- 字段一致性、分页总数、状态转换、权限边界
```mermaid
flowchart TD
Start(["开始: 发送请求"]) --> Patch["patch 业务方法"]
Patch --> CallAPI["调用 /queries/* 路由"]
CallAPI --> Resp{"响应状态码"}
Resp --> |201/200| AssertOK["断言返回字段与业务语义"]
Resp --> |403/404| AssertErr["断言错误消息与权限边界"]
AssertOK --> End(["结束"])
AssertErr --> End
```
图表来源
- [tests/test_queries.py:30-70](file://tests/test_queries.py#L30-L70)
- [tests/test_queries.py:127-139](file://tests/test_queries.py#L127-L139)
章节来源
- [tests/test_queries.py:29-154](file://tests/test_queries.py#L29-L154)
### 引用数据与报告测试
- 关键点
- mock服务返回结构化数据验证列表、统计聚合与CSV导出的响应头与内容
- 推荐断言
- 统计字段存在性与合理性、CSV内容片段包含关键字段
章节来源
- [tests/test_citations.py:23-93](file://tests/test_citations.py#L23-L93)
### 引用检测引擎单元测试
- 关键点
- 测试品牌匹配器的精确、别名、模糊匹配与无匹配场景,以及引用位置与置信度
- 测试竞争品牌检测器对不同行业类别的识别
- 推荐断言
- 匹配结果布尔值、匹配类型、置信度范围、位置编号与上下文片段
```mermaid
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 : "组合使用"
```
图表来源
- [tests/test_citation_engine.py:6-54](file://tests/test_citation_engine.py#L6-L54)
- [backend/app/workers/citation_engine.py:19-146](file://backend/app/workers/citation_engine.py#L19-L146)
章节来源
- [tests/test_citation_engine.py:1-54](file://tests/test_citation_engine.py#L1-L54)
- [backend/app/workers/citation_engine.py:19-146](file://backend/app/workers/citation_engine.py#L19-L146)
## 依赖分析
- 测试与应用的耦合
- 通过依赖覆盖与patch解耦具体实现细节提升测试稳定性
- 会话级mock调度器避免真实任务执行保证测试幂等性
- 外部依赖
- PostgreSQL与Redis通过docker-compose提供测试可选择独立数据库或内存数据库如aiosqlite以加速
- 日志与可观测性
- 引擎与调度器使用标准日志便于定位问题Alembic日志级别较低避免噪声
```mermaid
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](file://tests/conftest.py#L19-L25)
- [backend/app/main.py:10-21](file://backend/app/main.py#L10-L21)
- [backend/app/workers/scheduler.py:25-95](file://backend/app/workers/scheduler.py#L25-L95)
- [backend/app/workers/citation_engine.py:148-309](file://backend/app/workers/citation_engine.py#L148-L309)
章节来源
- [tests/conftest.py:19-25](file://tests/conftest.py#L19-L25)
- [backend/app/main.py:10-21](file://backend/app/main.py#L10-L21)
- [backend/app/workers/scheduler.py:25-95](file://backend/app/workers/scheduler.py#L25-L95)
- [backend/app/workers/citation_engine.py:148-309](file://backend/app/workers/citation_engine.py#L148-L309)
- [backend/alembic.ini:115-149](file://backend/alembic.ini#L115-L149)
## 性能考虑
- 测试速度
- 使用会话级fixture减少重复初始化成本
- 将外部依赖(数据库、缓存)置于容器中,避免每次重启
- 并发与异步
- 使用pytest-asyncio与AsyncClient确保异步测试稳定
- 覆盖率
- 建议优先达到以下目标行覆盖率≥80%、分支覆盖率≥70%、功能覆盖率≥90%
- 对关键路径(认证、查询、引用检测)进行重点覆盖
## 故障排查指南
- 常见问题
- 未覆盖依赖导致未认证访问失败:确认在测试中正确注入依赖覆盖
- patch目标不匹配核对被patch的服务方法签名与调用路径
- 调度器干扰确保会话级mock调度器生效
- 调试技巧
- 使用pytest调试选项--pdb进入调试器、-v详细输出、-s打印日志
- 启用更详细的日志:在测试前设置日志级别,关注引擎与调度器日志
- 分离数据库使用独立测试数据库或内存数据库如aiosqlite以避免数据污染
- 错误排查步骤
- 逐个缩小patch范围确认业务方法是否被正确替换
- 检查JWT生成与解析流程确保令牌有效
- 校验路由前缀与路径参数避免404
章节来源
- [tests/conftest.py:19-25](file://tests/conftest.py#L19-L25)
- [tests/test_auth.py:88-104](file://tests/test_auth.py#L88-L104)
- [backend/app/api/deps.py:16-43](file://backend/app/api/deps.py#L16-L43)
## 结论
通过统一的命名与结构规范、完善的夹具与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并生成覆盖率报告
- 上传覆盖率与测试报告
- 参考文件
- 后端依赖清单:[backend/requirements.txt:31-35](file://backend/requirements.txt#L31-L35)
- 服务编排:[docker-compose.yml:1-71](file://docker-compose.yml#L1-L71)
章节来源
- [backend/requirements.txt:31-35](file://backend/requirements.txt#L31-L35)
- [docker-compose.yml:1-71](file://docker-compose.yml#L1-L71)
### 测试环境管理
- 数据库
- 开发使用docker-compose提供的PostgreSQL
- 测试可选独立测试库或内存数据库如aiosqlite避免跨测试污染
- 缓存
- Redis用于任务调度与缓存测试中可直接使用容器版本
- 依赖注入
- 通过conftest中的依赖覆盖确保测试中始终使用mock用户与禁用真实调度器
章节来源
- [docker-compose.yml:1-71](file://docker-compose.yml#L1-L71)
- [tests/conftest.py:19-50](file://tests/conftest.py#L19-L50)
### 测试数据管理
- 建议
- 使用fixture生成稳定的UUID与时间戳确保可重复性
- 对于复杂对象查询、引用记录在fixture中集中定义字段默认值
- 对于CSV导出等场景使用固定字符串片段进行断言避免动态内容导致脆弱断言
章节来源
- [tests/test_queries.py:10-26](file://tests/test_queries.py#L10-L26)
- [tests/test_citations.py:8-20](file://tests/test_citations.py#L8-L20)
- [tests/test_citations.py:76-93](file://tests/test_citations.py#L76-L93)
### 测试报告生成
- 建议
- 使用pytest-cov生成HTML或XML报告便于CI集成
- 在CI中保留报告产物便于回溯历史趋势
- 日志配置
- Alembic日志级别已较低可在测试中临时提高日志级别以辅助排查
章节来源
- [backend/alembic.ini:115-149](file://backend/alembic.ini#L115-L149)