geo/.qoder/repowiki/zh/content/测试策略/单元测试.md

23 KiB
Raw Blame History

单元测试

**本文档引用的文件** - [tests/conftest.py](file://tests/conftest.py) - [tests/test_auth.py](file://tests/test_auth.py) - [tests/test_citation_engine.py](file://tests/test_citation_engine.py) - [tests/test_citations.py](file://tests/test_citations.py) - [tests/test_queries.py](file://tests/test_queries.py) - [backend/app/main.py](file://backend/app/main.py) - [backend/app/api/deps.py](file://backend/app/api/deps.py) - [backend/app/api/auth.py](file://backend/app/api/auth.py) - [backend/app/api/citations.py](file://backend/app/api/citations.py) - [backend/app/api/queries.py](file://backend/app/api/queries.py) - [backend/app/workers/citation_engine.py](file://backend/app/workers/citation_engine.py) - [backend/app/services/auth.py](file://backend/app/services/auth.py) - [backend/app/models/user.py](file://backend/app/models/user.py) - [backend/app/models/citation_record.py](file://backend/app/models/citation_record.py)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖分析
  7. 性能考虑
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本文件系统性梳理 GEO 项目的单元测试实现覆盖认证模块、引用引擎、查询处理与引用数据四大模块的测试用例设计与最佳实践。文档重点说明测试夹具fixture的使用方法如 mock_user、auth_token、auth_headers、override_get_current_user断言编写方式与覆盖率建议并提供异步函数、依赖注入与错误处理的测试示例路径与流程图。

项目结构

测试目录位于仓库根目录 tests/,采用按功能分模块组织的方式,分别对应后端 API 层与核心业务组件:

  • 认证模块测试tests/test_auth.py
  • 引用引擎测试tests/test_citation_engine.py
  • 查询处理测试tests/test_queries.py
  • 引用数据测试tests/test_citations.py
  • 测试夹具与异步客户端tests/conftest.py
  • 后端应用入口与路由backend/app/main.py、backend/app/api/*.py
  • 核心业务组件backend/app/workers/citation_engine.py
  • 服务层与模型backend/app/services/、backend/app/models/
graph TB
subgraph "测试层"
T1["tests/test_auth.py"]
T2["tests/test_citation_engine.py"]
T3["tests/test_queries.py"]
T4["tests/test_citations.py"]
C["tests/conftest.py"]
end
subgraph "后端应用"
M["backend/app/main.py"]
A1["backend/app/api/auth.py"]
A2["backend/app/api/queries.py"]
A3["backend/app/api/citations.py"]
D["backend/app/api/deps.py"]
S1["backend/app/services/auth.py"]
W["backend/app/workers/citation_engine.py"]
U["backend/app/models/user.py"]
CR["backend/app/models/citation_record.py"]
end
T1 --> A1
T2 --> W
T3 --> A2
T4 --> A3
T1 --> D
T2 --> W
T3 --> D
T4 --> D
A1 --> S1
A2 --> D
A3 --> D
D --> U
W --> CR
M --> A1
M --> A2
M --> A3
C --> M

图表来源

章节来源

核心组件

本节概述各模块测试关注点与测试夹具的作用范围。

  • 认证模块测试tests/test_auth.py

    • 注册成功/重复邮箱、登录成功/密码错误、用户信息获取(含未认证场景)
    • 使用夹具mock_registered_user、async_client、patch 注入服务层行为
    • 关键断言HTTP 状态码、响应体字段、错误详情消息
  • 引用引擎测试tests/test_citation_engine.py

    • 品牌匹配器精确/别名/模糊/无匹配;竞争品牌检测;引用位置与上下文提取
    • 使用夹具:直接构造类实例进行纯函数式断言,无需外部依赖
  • 查询处理测试tests/test_queries.py

    • 创建查询成功/超出限额、列出查询、更新查询、删除查询、查询不存在/不属于他人
    • 使用夹具mock_query、async_client、patch 服务层返回值或异常
  • 引用数据测试tests/test_citations.py

    • 获取引用列表、统计信息、CSV 导出MIME 类型、附件头、内容片段)
  • 测试夹具tests/conftest.py

    • mock_scheduler屏蔽后台调度器避免真实任务影响测试
    • mock_user模拟认证用户对象包含 id、邮箱、计划、配额等
    • override_get_current_user重写依赖 get_current_user使路由自动获得认证用户
    • auth_token基于 mock_user 生成 JWT
    • auth_headers组装 Authorization Bearer 头
    • async_clientASGI 异步 HTTP 客户端,用于端到端测试

章节来源

架构总览

下图展示测试夹具与被测组件之间的交互关系,以及异步 HTTP 客户端如何驱动 FastAPI 路由与依赖注入。

sequenceDiagram
participant Test as "测试用例"
participant Fixture as "测试夹具(conftest.py)"
participant Client as "AsyncClient"
participant App as "FastAPI 应用(main.py)"
participant Dep as "依赖注入(deps.py)"
participant Service as "服务层(API 路由)"
participant Model as "模型(models)"
Test->>Fixture : 请求夹具(mock_user/override_get_current_user/...)
Fixture-->>Test : 返回夹具对象
Test->>Client : 发起 HTTP 请求
Client->>App : ASGI 请求
App->>Dep : 解析 OAuth2 Bearer Token
Dep-->>App : 返回当前用户(get_current_user)
App->>Service : 调用路由处理器
Service->>Model : 数据库读写/查询
Service-->>App : 返回响应数据
App-->>Client : HTTP 响应
Client-->>Test : 断言结果

图表来源

详细组件分析

认证模块测试

  • 测试要点
    • 注册接口:成功返回 201响应包含邮箱与姓名重复邮箱抛出 400
    • 登录接口:成功返回 200包含 access_token 与用户信息;错误凭据返回 401
    • 用户信息接口:已认证返回 200未认证返回 401
  • 测试夹具
    • mock_registered_user模拟注册成功的用户对象
    • override_get_current_user通过依赖覆盖提供认证用户
    • auth_headers携带 Bearer Token 的请求头
    • async_clientASGI 异步客户端
  • 断言策略
    • 状态码断言201/200/400/401
    • 响应体字段断言access_token、token_type、user.email 等
    • 错误详情断言detail 中包含特定提示
  • 异步与依赖注入示例路径
sequenceDiagram
participant T as "测试用例(test_auth.py)"
participant F as "夹具(conftest.py)"
participant AC as "AsyncClient"
participant R as "路由(auth.py)"
participant S as "服务(auth.py)"
participant DB as "数据库"
T->>F : 获取 mock_registered_user/override_get_current_user/auth_headers
T->>AC : POST /api/v1/auth/register
AC->>R : 路由处理器
R->>S : register_user(...)
S->>DB : 查询/插入用户
S-->>R : 返回用户对象
R-->>AC : 201 + 用户信息
AC-->>T : 断言状态码与字段

图表来源

章节来源

引用引擎测试

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 : "在引擎中协作"

图表来源

章节来源

查询处理测试

sequenceDiagram
participant T as "测试用例(test_queries.py)"
participant F as "夹具(conftest.py)"
participant AC as "AsyncClient"
participant R as "路由(queries.py)"
participant S as "服务层"
participant DB as "数据库"
T->>F : 获取 mock_query/override_get_current_user/auth_headers
T->>AC : POST /api/v1/queries/
AC->>R : 路由处理器
R->>S : create_query(...)
S->>DB : 插入查询记录
S-->>R : 返回查询对象
R-->>AC : 201 + 查询信息
AC-->>T : 断言状态码与字段

图表来源

章节来源

引用数据测试

  • 测试要点
    • 获取引用列表:返回 items 与 total包含 platform 与 cited 等字段
    • 统计信息:返回总量、引用率、按平台分布与趋势
    • CSV 导出:返回 text/csv 内容类型与附件头,内容包含平台信息
  • 测试夹具
    • mock_citation_record模拟引用记录对象
    • override_get_current_user、auth_headers、async_client
  • 断言策略
    • 状态码断言200
    • 响应体断言total、items、by_platform、trend 等
    • 响应头断言Content-Type 以 text/csv 开头、Content-Disposition 含 attachment
  • 示例路径
sequenceDiagram
participant T as "测试用例(test_citations.py)"
participant F as "夹具(conftest.py)"
participant AC as "AsyncClient"
participant R as "路由(citations.py)"
participant S as "服务层"
participant DB as "数据库"
T->>F : 获取 mock_citation_record/override_get_current_user/auth_headers
T->>AC : GET /api/v1/citations/stats
AC->>R : 路由处理器
R->>S : get_citation_stats(...)
S->>DB : 聚合统计
S-->>R : 返回统计结果
R-->>AC : 200 + 统计信息
AC-->>T : 断言字段与头

图表来源

章节来源

测试夹具详解与最佳实践

  • mock_scheduler
    • 作用:屏蔽后台调度器,避免真实任务启动影响测试稳定性
    • 使用:在会话级 autouse fixture 中替换 app.main.query_scheduler 的 start/shutdown
    • 参考路径:tests/conftest.py:19-26
  • mock_user
    • 作用:提供认证用户对象,包含 id、email、name、plan、max_queries、is_active 等
    • 使用:作为 override_get_current_user 的数据源
    • 参考路径:tests/conftest.py:29-39
  • override_get_current_user
    • 作用:重写依赖 get_current_user使路由自动解析到 mock_user
    • 使用:在测试函数参数中注入,结束后清理依赖覆盖
    • 参考路径:tests/conftest.py:42-50
  • auth_token
    • 作用:基于 mock_user 的 id 生成 JWT
    • 使用:配合 auth_headers 进行认证请求
    • 参考路径:tests/conftest.py:54-56
  • auth_headers
    • 作用:组装 Authorization: Bearer 请求头
    • 使用GET/POST/PUT/DELETE 等请求统一携带
    • 参考路径:tests/conftest.py:60-62
  • async_client
    • 作用ASGI 异步 HTTP 客户端,用于端到端测试
    • 使用with 上下文管理器确保生命周期内正确创建与释放
    • 参考路径:tests/conftest.py:65-71

最佳实践

  • 尽量使用 patch 替换服务层或外部依赖,避免真实网络或数据库调用
  • 对于需要认证的路由,优先使用 override_get_current_user 与 auth_headers
  • 对于后台任务,使用 mock_scheduler 屏蔽真实调度器
  • 对于异步函数,使用 pytest.mark.asyncio 并通过 AsyncClient 发起请求
  • 清理:在依赖覆盖后及时 pop避免影响其他测试

章节来源

异步函数、依赖注入与错误处理测试示例

章节来源

测试数据准备与清理

章节来源

依赖分析

  • 测试对应用层的依赖
    • 测试通过 ASGI AsyncClient 直接调用 FastAPI 路由,路由再依赖 get_current_user 解析 JWT
    • get_current_user 依赖 OAuth2PasswordBearer 与 verify_token最终查询数据库 User 表
  • 测试对服务层的依赖
    • 通过 patch 替换服务层函数(如 register_user、authenticate_user、create_query 等)
  • 测试对业务组件的依赖
    • 引擎测试直接构造 BrandMatcher/CompetitorDetector不依赖外部服务
graph LR
T["测试用例"] --> C["conftest.py 夹具"]
C --> A["FastAPI 路由"]
A --> D["依赖注入(get_current_user)"]
D --> S["服务层"]
S --> DB["数据库"]
T --> E["引用引擎(直接类实例)"]

图表来源

章节来源

性能考虑

  • 使用 ASGI AsyncClient 避免真实网络往返,提升测试速度
  • 通过 patch 替换外部服务调用,减少 IO 与等待时间
  • 使用 mock_scheduler 屏蔽后台任务,避免并发与定时任务干扰
  • 对纯函数(引擎类)直接实例化断言,避免数据库与网络依赖

故障排查指南

章节来源

结论

本测试体系通过夹具与 patch 有效隔离外部依赖,结合 ASGI 异步客户端完成端到端验证。认证、查询、引用数据三大模块均覆盖成功与错误路径,引擎模块以纯函数形式保证可测试性与高内聚。建议持续补充边界条件与并发场景测试,逐步提升整体覆盖率。

附录