> **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`(编号段、水位、状态)