16 KiB
16 KiB
集成测试
**本文引用的文件** - [tests/conftest.py](file://tests/conftest.py) - [backend/app/main.py](file://backend/app/main.py) - [backend/app/database.py](file://backend/app/database.py) - [backend/app/config.py](file://backend/app/config.py) - [docker-compose.yml](file://docker-compose.yml) - [tests/test_auth.py](file://tests/test_auth.py) - [tests/test_queries.py](file://tests/test_queries.py) - [tests/test_citations.py](file://tests/test_citations.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/queries.py](file://backend/app/api/queries.py) - [backend/app/api/citations.py](file://backend/app/api/citations.py) - [backend/app/workers/scheduler.py](file://backend/app/workers/scheduler.py) - [backend/app/services/auth.py](file://backend/app/services/auth.py)目录
简介
本文件面向GEO项目的集成测试,系统性阐述端到端测试的实现方法与最佳实践,覆盖以下方面:
- FastAPI应用测试:通过ASGI传输层在内存中启动应用,使用异步HTTP客户端进行端到端请求验证。
- 数据库连接测试:基于异步SQLAlchemy引擎与会话管理,确保依赖注入与数据库交互正确。
- 外部服务集成测试:通过依赖注入覆盖与模拟(patch)实现对外部平台(如wenxin、kimi)的模拟调用。
- 测试环境配置:测试数据库、异步客户端、依赖注入覆盖与后台调度器的处理策略。
- 完整用户工作流:从认证到查询创建、执行到结果返回的端到端流程验证。
- 数据库事务与测试数据隔离:通过依赖注入覆盖与会话生命周期管理保障隔离性。
- 性能与负载测试:提供可扩展的测试方法与建议。
项目结构
GEO采用前后端分离架构,后端使用FastAPI + SQLAlchemy异步ORM,测试位于tests目录,通过pytest与pytest-asyncio驱动异步测试。数据库与缓存服务通过Docker Compose编排,便于本地与CI环境一致化。
graph TB
subgraph "测试环境"
TC["测试客户端<br/>AsyncClient(ASGI)"]
CFG["测试配置<br/>Settings(.env)"]
DC["Docker Compose<br/>db, redis, backend, frontend"]
end
subgraph "后端"
APP["FastAPI 应用<br/>app.main"]
DEPS["依赖注入<br/>api.deps"]
AUTH["认证路由<br/>api.auth"]
QUERIES["查询路由<br/>api.queries"]
CITATIONS["引用路由<br/>api.citations"]
SCHED["调度器<br/>workers.scheduler"]
DB["数据库引擎/会话<br/>database.py"]
end
DC --> APP
CFG --> APP
TC --> APP
APP --> AUTH
APP --> QUERIES
APP --> CITATIONS
APP --> SCHED
AUTH --> DB
QUERIES --> DB
CITATIONS --> DB
DEPS --> DB
图表来源
- backend/app/main.py:1-48
- backend/app/api/deps.py:1-43
- backend/app/api/auth.py:1-43
- backend/app/api/queries.py:1-86
- backend/app/api/citations.py:1-78
- backend/app/database.py:1-29
- backend/app/workers/scheduler.py:1-95
- docker-compose.yml:1-71
章节来源
核心组件
- 异步HTTP客户端与ASGI传输层:通过ASGITransport在内存中启动FastAPI应用,避免真实网络开销,提升测试速度与稳定性。
- 依赖注入覆盖:使用app.dependency_overrides在测试中替换真实依赖(如认证用户),以模拟不同业务场景。
- 认证与令牌:通过create_access_token生成JWT,配合OAuth2PasswordBearer在路由中校验令牌。
- 数据库引擎与会话:异步SQLAlchemy引擎与会话工厂,确保测试中数据库交互的可控性与隔离性。
- 后台调度器:QueryScheduler负责周期性检查并触发查询任务,测试中通过mock屏蔽真实调度,防止后台任务干扰测试。
章节来源
- tests/conftest.py:19-71
- backend/app/api/deps.py:16-43
- backend/app/services/auth.py:24-34
- backend/app/database.py:6-29
- backend/app/workers/scheduler.py:25-95
架构总览
下图展示测试视角下的端到端调用链路,从异步客户端发起请求,经由FastAPI路由、依赖注入、服务层,最终访问数据库或外部平台(通过模拟)。
sequenceDiagram
participant Test as "测试客户端"
participant App as "FastAPI 应用"
participant Dep as "依赖注入<br/>get_current_user"
participant Auth as "认证路由<br/>/api/v1/auth/*"
participant Queries as "查询路由<br/>/api/v1/queries/*"
participant Citations as "引用路由<br/>/api/v1/citations/*"
participant DB as "数据库引擎/会话"
Test->>App : "POST /api/v1/auth/register"
App->>Dep : "解析令牌/校验用户"
App->>Auth : "注册逻辑"
Auth->>DB : "写入用户"
DB-->>Auth : "提交成功"
Auth-->>Test : "201 用户信息"
Test->>App : "POST /api/v1/auth/login"
App->>Auth : "登录校验"
Auth-->>Test : "200 {access_token}"
Test->>App : "POST /api/v1/queries/"
App->>Dep : "校验令牌并解析用户"
App->>Queries : "创建查询"
Queries->>DB : "插入查询记录"
DB-->>Queries : "提交成功"
Queries-->>Test : "201 查询详情"
Test->>App : "GET /api/v1/citations/?query_id=..."
App->>Dep : "校验令牌并解析用户"
App->>Citations : "查询引用数据"
Citations->>DB : "读取引用记录"
DB-->>Citations : "返回数据"
Citations-->>Test : "200 引用列表"
图表来源
- tests/test_auth.py:25-104
- tests/test_queries.py:29-154
- tests/test_citations.py:23-93
- backend/app/api/auth.py:13-43
- backend/app/api/queries.py:15-86
- backend/app/api/citations.py:25-78
- backend/app/api/deps.py:16-43
- backend/app/database.py:23-29
详细组件分析
测试环境与配置
- 测试数据库与外部服务:通过Docker Compose启动PostgreSQL与Redis,确保测试与生产环境一致;数据库URL与Redis URL在配置类中定义,便于在测试中覆盖。
- 异步客户端:使用ASGITransport在内存中启动FastAPI应用,避免网络抖动对测试的影响。
- 依赖注入覆盖:在conftest中定义override_get_current_user,使所有需要认证的路由在测试中自动获得模拟用户。
- 调度器屏蔽:通过mock_scheduler在测试期间禁用真实调度器,防止后台任务影响测试稳定性。
章节来源
认证与用户工作流测试
- 注册与登录:通过patch模拟注册与登录服务,断言状态码与响应字段,确保认证流程正确。
- 当前用户信息:在依赖注入覆盖下,调用/me接口返回当前用户信息。
- 未认证访问:移除依赖注入覆盖后,访问受保护路由应返回401。
sequenceDiagram
participant C as "测试客户端"
participant A as "认证路由"
participant S as "认证服务"
participant D as "数据库"
C->>A : "POST /api/v1/auth/register"
A->>S : "register_user()"
S->>D : "查询邮箱是否存在"
S->>D : "插入新用户"
D-->>S : "提交成功"
S-->>A : "返回用户"
A-->>C : "201 Created"
C->>A : "POST /api/v1/auth/login"
A->>S : "authenticate_user()"
S->>D : "查询用户并校验密码"
D-->>S : "返回用户"
S-->>A : "生成JWT"
A-->>C : "200 {access_token}"
图表来源
章节来源
查询管理端到端测试
- 创建查询:在依赖注入覆盖与令牌头下,调用创建接口,断言返回字段与状态码。
- 列表与详情:通过patch模拟服务层返回值,断言分页与字段一致性。
- 更新与删除:断言更新后的字段与删除后的状态码。
- 权限限制:当超过配额时,断言403错误与错误信息。
- 资源不存在:断言404错误与错误信息。
sequenceDiagram
participant C as "测试客户端"
participant Q as "查询路由"
participant S as "查询服务"
participant D as "数据库"
C->>Q : "POST /api/v1/queries/"
Q->>S : "create_query()"
S->>D : "插入查询记录"
D-->>S : "提交成功"
S-->>Q : "返回查询"
Q-->>C : "201 Created"
C->>Q : "GET /api/v1/queries/?skip=&limit="
Q->>S : "get_queries()"
S->>D : "查询记录"
D-->>S : "返回列表"
S-->>Q : "返回分页"
Q-->>C : "200 OK"
C->>Q : "PUT /api/v1/queries/{id}"
Q->>S : "update_query()"
S->>D : "更新记录"
D-->>S : "提交成功"
S-->>Q : "返回更新后记录"
Q-->>C : "200 OK"
C->>Q : "DELETE /api/v1/queries/{id}"
Q->>S : "delete_query()"
S->>D : "删除记录"
D-->>S : "提交成功"
S-->>Q : "返回True"
Q-->>C : "204 No Content"
图表来源
章节来源
引用数据与统计导出测试
- 引用列表:断言分页、字段与过滤参数生效。
- 统计信息:断言总量、引用率、按平台分布与趋势数据。
- CSV导出:断言内容类型、附件头与CSV内容片段。
sequenceDiagram
participant C as "测试客户端"
participant R as "引用路由"
participant RS as "引用服务"
participant D as "数据库"
C->>R : "GET /api/v1/citations/?query_id=&platform=&start_date=&end_date=&skip=&limit="
R->>RS : "get_citations()"
RS->>D : "查询引用记录"
D-->>RS : "返回列表"
RS-->>R : "返回分页"
R-->>C : "200 OK"
C->>R : "GET /api/v1/citations/stats?query_id="
R->>RS : "get_citation_stats()"
RS->>D : "聚合统计"
D-->>RS : "返回统计"
RS-->>R : "返回统计"
R-->>C : "200 OK"
C->>R : "GET /api/v1/reports/export/csv?query_id="
R->>RS : "export_citations_csv()"
RS-->>R : "返回CSV内容"
R-->>C : "200 OK, text/csv, attachment"
图表来源
章节来源
数据库事务管理与测试数据隔离
- 会话生命周期:get_db提供异步会话生成与关闭,确保每次请求在独立会话中执行,避免跨请求污染。
- 依赖注入覆盖:通过app.dependency_overrides在测试中替换get_current_user,保证路由依赖始终可用且可控。
- 事务隔离:测试中通过patch模拟服务层,不直接写入真实数据;若需真实写入,应在测试事务中回滚或使用独立测试库。
flowchart TD
Start(["测试开始"]) --> Override["依赖注入覆盖<br/>get_current_user"]
Override --> Session["获取异步会话<br/>get_db()"]
Session --> Exec["执行路由/服务逻辑"]
Exec --> Commit{"提交/回滚?"}
Commit --> |提交| Close["关闭会话"]
Commit --> |回滚| Close
Close --> End(["测试结束"])
图表来源
章节来源
外部服务集成测试
- 平台模拟:通过patch将平台调用替换为模拟实现,断言触发查询任务与返回的任务信息。
- 调度器屏蔽:在测试期间mock调度器,避免真实后台任务执行,确保测试确定性。
章节来源
依赖分析
- 应用生命周期:FastAPI应用在lifespan中启动调度器,并在关闭时优雅shutdown。
- 路由依赖:认证与查询/引用路由均依赖get_current_user与get_db,形成清晰的依赖链。
- 服务层:认证、查询、引用服务分别封装业务逻辑,便于在测试中通过patch进行替换。
graph LR
APP["FastAPI 应用"] --> LIFE["lifespan 启停调度器"]
APP --> AUTH["认证路由"]
APP --> QUERIES["查询路由"]
APP --> CITATIONS["引用路由"]
AUTH --> DEPS["get_current_user"]
QUERIES --> DEPS
CITATIONS --> DEPS
DEPS --> DB["get_db/AsyncSession"]
图表来源
章节来源
性能考虑
- 内存测试:使用ASGI传输层与异步客户端,避免网络开销,提升测试吞吐量。
- 调度器控制:在测试中禁用真实调度器,减少后台任务对测试性能的影响。
- 数据库隔离:通过独立测试库或事务回滚策略,避免并发测试导致的数据竞争。
- 负载测试建议:可在现有测试基础上扩展压力测试脚本,结合异步客户端批量发送请求,监控响应时间与错误率;注意在测试环境中使用独立数据库实例以避免影响生产数据。
故障排查指南
- 401未认证:确认测试中是否正确设置依赖注入覆盖与Authorization头。
- 403权限不足:检查配额限制逻辑与用户计划,确保测试数据符合预期。
- 404资源不存在:确认查询ID与用户归属,确保服务层返回None时路由抛出404。
- 调度器干扰:若出现不可预期的后台任务行为,确认是否启用了mock_scheduler。
章节来源
- tests/test_auth.py:88-104
- tests/test_queries.py:50-71
- tests/test_queries.py:127-154
- tests/conftest.py:19-26
结论
本文档提供了GEO项目集成测试的完整方法论与实施细节,涵盖FastAPI应用测试、数据库连接、外部服务模拟、端到端用户工作流验证、事务与隔离策略以及性能与负载测试建议。通过依赖注入覆盖与异步客户端,测试具备高可靠性与可维护性;借助Docker Compose与配置类,测试环境与生产环境保持一致,便于持续集成与部署。
附录
- 建议在CI中增加:
- 数据库迁移与初始化脚本的集成测试。
- Redis健康检查与连接超时重试策略验证。
- 前后端联调测试,确保API与前端交互稳定。