374 lines
11 KiB
Markdown
374 lines
11 KiB
Markdown
> **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 规范
|
||
|
||
- R2:mock `boto3.client`
|
||
- Redis:fakeredis / 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`(编号段、水位、状态)
|