# 测试最佳实践

<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)