Files
nexus/Project/fonrey/TECH_STACK/测试规范.md
2026-04-30 20:33:51 +08:00

374 lines
11 KiB
Markdown
Raw 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.
> **For AI assistants**: Read this entire file before writing any test code. All decisions here are final. Do not suggest alternatives unless asked.
# Fonrey 测试规范TEST_SPEC
**版本**: 1.2
**项目**: Fonrey 房产经纪管理系统
**技术栈**: Django 4.x + django-tenants + PostgreSQL 16 + Redis + Celery + HTMX + Playwright
**关联文档**: `TECH_STACK/TECH_STACK.md``PRD/TASK.md``TEST_CASES/TEST_CASE_ID_SPEC.md``TEST_CASES/TEST_CASE_REGISTRY.md`、各模块技术方案(登录/权限/房源/客源/楼盘/组织人事/系统管理)
**最后更新**: 2026-04-30
---
## 变更历史
| 日期 | 变更人 | 变更内容 |
|---|---|---|
| 2026-04-30 | Atlas | 补充“变更历史”章节(文档治理) |
## 一、文档定位与边界
本文件定义项目统一测试标准:
1. 测试分层与覆盖率目标
2. 测试目录与夹具fixture约定
3. 多租户与 HTMX 场景测试规范
4. CI 执行基线与失败处理流程
5. AI 辅助开发的“测试随功能交付”硬约束
> 本文件不替代模块级测试设计。每个业务模块的案例细节以对应技术方案和 PRD AC 为准。
---
## 二、测试目标与覆盖基线
Fonrey 采用 AI 驱动迭代,测试是质量兜底。所有 P0 User Story 必须做到“功能 + 测试”同步交付。
### 2.1 覆盖率目标
| 层级 | 最低目标 |
|---|---|
| `core/` 基础模块 | ≥ 90% |
| `apps/*/services/` 业务逻辑层 | ≥ 80% |
| `apps/*/views*` 接口与视图层 | ≥ 70% |
| `apps/*/tasks.py` 异步任务 | ≥ 70% |
| E2E 核心测试用例 | 覆盖指定核心用例并全部通过 |
### 2.2 质量门禁
- 每个 P0 US 对应至少一个集成测试场景集。
- PR 合并前:单元 + 集成必须全绿。
- `main/develop`:每日自动跑全量(含 E2E 核心测试用例)。
---
## 三、测试分层架构
```
┌─────────────────────────────────────────┐
│ E2E 测试(测试用例) │ ← Playwright
├─────────────────────────────────────────┤
│ 集成测试HTTP / View / Service / DB │ ← pytest-django + TenantClient
├─────────────────────────────────────────┤
│ 单元测试(纯逻辑) │ ← pytest + factory_boy + mock
└─────────────────────────────────────────┘
```
### 3.1 单元测试
- 目标:服务层、工具层、任务函数的逻辑正确性。
- 约束:不启动真实 HTTP不依赖外部网络。
### 3.2 集成测试
- 目标验证完整请求链路View → Service → DB
- 约束:必须使用 `TenantClient`,禁止 Django 原生 `Client()`
### 3.3 E2E 测试
- 目标:验证真实用户关键路径。
- 约束:只覆盖核心测试用例,避免把所有细节都堆到 E2E。
---
## 四、工具选型与依赖
| 类型 | 工具 | 版本建议 | 用途 |
|---|---|---|---|
| 测试框架 | `pytest` | ≥ 8.x | 统一运行器 |
| Django 集成 | `pytest-django` | ≥ 4.x | DB/Client/Settings |
| 数据工厂 | `factory_boy` | ≥ 3.x | 生成测试数据 |
| 假数据 | `Faker` | ≥ 25.x | 中文业务数据 |
| Mock | `pytest-mock` | ≥ 3.x | 外部依赖打桩 |
| HTTP Mock | `responses` | ≥ 0.25.x | 三方 HTTP 隔离 |
| 覆盖率 | `pytest-cov` | ≥ 5.x | coverage 报告 |
| 并行 | `pytest-xdist` | ≥ 3.x | 加速单测/集成 |
| E2E | `playwright` + `pytest-playwright` | ≥ 1.44 / ≥ 0.5 | 浏览器自动化 |
安装基线:
```bash
pip install -r requirements/test.txt
playwright install chromium
```
---
## 五、目录结构约定
```text
tests/
├── conftest.py
├── settings_test.py
├── factories/
│ ├── tenant_factory.py
│ ├── account_factory.py
│ ├── permission_factory.py
│ ├── complex_factory.py
│ ├── property_factory.py
│ ├── client_factory.py
│ └── org_factory.py
├── unit/
│ ├── test_encryption.py
│ ├── test_soft_delete.py
│ ├── test_*_service.py
│ └── test_celery_tasks.py
├── integration/
│ ├── account/test_us_account.py
│ ├── permission/test_us_permission.py
│ ├── complex/test_us_complex.py
│ ├── property/test_us_property.py
│ ├── client/test_us_client.py
│ ├── org/test_us_org.py
│ └── setting/test_us_setting.py
└── e2e/
├── conftest.py
├── test_journey_login.py
├── test_journey_property.py
├── test_journey_client.py
├── test_journey_permission.py
└── test_journey_onboarding.py
```
---
## 六、多租户测试约定(强制)
### 6.1 核心原则
1. 所有 DB 测试在租户 schema 上下文执行。
2. 业务数据禁止直接在 `public` schema 断言。
3. 事务隔离默认开启,测试间不得共享可变状态。
4. 测试请求必须经 `TenantClient` 发出。
### 6.2 标准 fixture最小集合
- `tenant`
- `tenant_client`
- `admin_user`
- `staff_user`
- `authenticated_client`
### 6.3 禁止事项
- 禁止手工 `SET search_path`
- 禁止跨租户数据断言
- 禁止在集成测试用 Django 原生 `Client()`
---
## 七、单元测试规范
### 7.1 覆盖范围
| 代码范围 | 示例文件 |
|---|---|
| `core/encryption.py` | `tests/unit/test_encryption.py` |
| `core/models/base.py` | `tests/unit/test_soft_delete.py` |
| `apps/*/services/` | `tests/unit/test_*_service.py` |
| `apps/*/tasks.py` | `tests/unit/test_celery_tasks.py` |
### 7.2 Celery 测试模式
`tests/settings_test.py` 必须启用:
```python
CELERY_TASK_ALWAYS_EAGER = True
CELERY_TASK_EAGER_PROPAGATES = True
```
并统一使用:
```python
result = some_task.apply(args=[...])
```
### 7.3 PII 相关必测点
- 密文不等于明文
- 同明文重复加密产生不同密文(随机 nonce
- 解密结果与原文一致
- 哈希索引稳定(同明文同 hash
---
## 八、集成测试规范
### 8.1 请求模式
| 类型 | Header | 预期 |
|---|---|---|
| 普通页面请求 | 无 | 完整 HTML |
| HTMX 局刷请求 | `HTTP_HX_REQUEST=true` | HTML 片段 |
### 8.2 权限覆盖最小集
每个受保护接口必须覆盖:
1. 有权限:`200`
2. 无权限:`403`
3. 未登录:`302`
### 8.3 User Story 映射基线
| US 范围 | 测试文件 |
|---|---|
| US-ACCOUNT-001~003 | `tests/integration/account/test_us_account.py` |
| US-PERMISSION-001~005 | `tests/integration/permission/test_us_permission.py` |
| US-COMPLEX-001~003 | `tests/integration/complex/test_us_complex.py` |
| US-PROPERTY-001~008 | `tests/integration/property/test_us_property.py` |
| US-CLIENT-001~017 | `tests/integration/client/test_us_client.py` |
| US-ORG-001~003 | `tests/integration/org/test_us_org.py` |
| US-SETTING-001 | `tests/integration/setting/test_us_setting.py` |
### 8.4 外部依赖 Mock 规范
- R2mock `boto3.client`
- Redisfakeredis / locmem cache
- 邮件locmem backend
- 第三方 HTTP`responses` 全量拦截
---
## 九、E2E 测试规范
### 9.1 核心测试用例(必须)
- E2E 覆盖对象采用“测试用例”定义不使用“旅程编号J-xx”。
- 每条 E2E 用例必须绑定全局唯一测试用例ID`TC-FON-XXXXXX`
- 当前登录模块核心用例以 `TEST_CASES/TEST_CASES_LOGIN_MODULE.md` 为准(`TC-FON-000001` ~ `TC-FON-000048`)。
- 其他模块(房源/客源/组织/权限等)按 `TEST_CASES/TEST_CASE_REGISTRY.md` 分配编号后补充。
### 9.2 Playwright 约束
- 默认 Chromium
- CI 使用 headless
- 禁止 `wait_for_timeout()` 固定等待
- 优先语义等待:`wait_for_url` / `expect(locator)` / `networkidle`
### 9.3 HTMX 页面注意事项
HTMX 更新后 URL 可不变,断言前必须等待请求完成:
```python
page.click('button:has-text("筛选")')
page.wait_for_load_state('networkidle')
```
---
## 十、测试用例编号与注册规范(强制)
### 10.1 编号规范
- 测试用例ID`TC-FON-XXXXXX`(全局唯一)
- 步骤ID`TC-FON-XXXXXX-SYY`
- 详见:`TEST_CASES/TEST_CASE_ID_SPEC.md`
### 10.2 注册流程
1. 新增用例前,先在 `TEST_CASES/TEST_CASE_REGISTRY.md` 查看下一个可用编号。
2. 先登记编号段(可先 `reserved`),再编写文档和代码。
3. 合并前状态改为 `active`,并更新“当前编号水位”。
### 10.3 强约束
- 不允许按模块重置编号。
- 不允许复用已废弃编号。
- 不允许未登记编号直接入库测试代码。
---
## 十一、测试配置基线
### 11.1 `pytest.ini`
```ini
[pytest]
DJANGO_SETTINGS_MODULE = tests.settings_test
python_files = test_*.py
addopts = --tb=short --strict-markers -q
markers =
unit
integration
e2e
slow
```
### 11.2 `tests/settings_test.py` 关键项
- Celery eager 模式开启
- Cache 使用测试后端locmem/fakeredis
- 邮件使用 locmem backend
- 媒体文件使用临时目录
- `DEBUG=False`(贴近生产)
---
## 十二、CI 自动化运行
### 12.1 触发策略
- 每日定时全量测试
- `main/develop` 每次 push 触发
### 12.2 流水线拆分
1. `unit-and-integration`
2. `e2e`(依赖前者成功后执行)
### 12.3 最低产物
- 覆盖率报告(终端 + 平台上传)
- E2E 失败截图 artifact
- 测试结果明细(至少含 `run_id``test_case_id``step_id``status``error_message``expected_result``actual_result`
---
## 十三、AI 协作测试要求
每个 User Story 实现后,必须同时补齐:
- Factory如缺失
- Service 单元测试(正常 + 至少2个边界
- View/API 集成测试(覆盖全部 AC
- 权限三态与 HTMX/普通请求双形态
修复顺序:
1. 先修功能代码
2. 仅当测试确实错误才改测试
3. 本地复跑通过后再提交
---
## 十四、禁止项Do NOT
- 禁止 Django 原生 `Client()` 进行租户集成测试
- 禁止固定等待(`sleep` / `wait_for_timeout`
- 禁止真实调用外部服务
- 禁止测试之间共享可变数据
- 禁止无权限/未登录场景缺失
- 禁止空测试占位后不补全
- 禁止未分配 `TC-FON-XXXXXX` 的匿名测试入库
---
## 十五、文档同步规则
- 新增/调整 User Story同步 `PRD/TASK.md` 与集成测试映射
- 模块 API 变更:同步对应模块技术方案
- 测试目录变更:同步本文件目录结构与 CI 脚本
- 新增测试基建fixture/工具):同步 `AGENTS.md` 与本文件
- 新增测试用例:同步 `TEST_CASES/TEST_CASE_REGISTRY.md`(编号段、水位、状态)