# 测试最佳实践 **本文档引用的文件** - [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) ## 目录 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)