测试规范
定位:改动必测、测试类型与覆盖、验证证据、根因判定纪律;独立评审与验收入口见 21-评审与验收规范.md。TDD 完整方法论见技能 .claude/skills/tdd、test-driven-development;根因诊断见 .claude/skills/systematic-debugging、diagnosing-bugs。
负面清单(Do Not)
- [禁止] 改动不跑测试就宣称完成
- [禁止] 测试失败时绕过/跳过测试提交代码
- [禁止] 每个测试文件各自造轮子(应复用测试辅助代码)
- [禁止] 测试依赖真实远程生产数据
- [禁止] seed 脚本面向生产环境执行
必须规则
- [必须] 改动必测:新增功能、修复 bug、数据结构变更,必须补充或更新对应测试;测试通过才算完成(DoD 之一)。
- [必须] 测试类型与覆盖:前端组件行为测试(而非实现细节)+ 服务测试(mock HTTP);后端 services 单元测试 + routes/controllers 集成测试(supertest);关键链路(登录鉴权/支付计费/数据删除)必须有测试覆盖;新功能覆盖率不得低于项目现有基线。
- [必须] 验收可追溯:PRD 验收标准编号化(AC-01、AC-02...),测试用例在描述中引用验收标准编号,实现需求可追溯(需求-测试矩阵见 docs/04-quality)。
- [必须] 测试代码封装规范(防重复造轮子):前端测试辅助统一放 src/testing/(mocks/fixtures/helpers),后端统一放 server/tests/helpers/(工具函数/公共断言);Mock 数据用工厂函数生成(MockUserFactory.create({...})),禁止散落对象字面量;测试文件与被测文件一一对应(user.service.ts → user.service.spec.ts),命名统一。
- [必须] 测试代码质量:测试代码同样遵循 prettier/命名规范;允许 describe/it/expect 专用约定。
- [必须] 测试环境隔离:测试跑独立 .env.test,不依赖远程生产数据;时间类边界用 fake timers,不 mock 无法可靠模拟的系统边界。
- [必须] 测试数据管理:测试数据与 seed 脚本禁止面向生产;测试后清理,不留脏数据。集成测试多轮运行会累积历史数据(如把种子数据挤出分页首页)——beforeAll 必须清理测试库;beforeDelete 云函数拦截一切物理删除(含 masterKey)时 Parse API 无法清库,专用测试库可用原生 SQL 直删并注释原因(仅限测试库)。
- [必须] 测试命令:前端 pnpm test(jest-preset-angular)、后端 pnpm test(jest + supertest);e2e 测试独立命令。
- [必须] 回归保障:修复 bug 时必须先写复现该 bug 的失败测试,再修复到通过,防止回归。
- [必须] ESM 依赖的集成测试用子进程方案:被测服务动态导入纯 ESM 依赖(如 parse-server 8 的 @parse/push-adapter)时,jest 的 CJS vm 沙箱无法加载(ERR_REQUIRE_ESM)——集成测试必须 spawn 启动真实服务进程(测试端口用 PORT 环境变量覆盖,如 3901),supertest 以 URL 断言;被测服务自身 SDK 回连地址(selfParseServerUrl)必须指向自身进程端口,禁止指向外部服务。
- [必须] 验证证据四类(无证据 = 未完成,禁止"看起来对就行"):命令+输出(命令文本+退出码+关键输出片段)/测试报告(用例数/通过数/失败用例清单)/截图+步骤(UI/浏览器验证:截图+操作步骤+环境版本)/API 响应(状态码+响应体+请求参数)。
- [必须] 根因判定门禁(先判后修):修复 bug 前必须先回答这是根因还是打补丁——修复能否消除所有同类触发路径?若只挡当前症状(如加 if (x) return)而未说明为何进入该状态即为补丁,必须继续追问上游成因;AI 推断的根因只能作为候选假设,每个候选根因至少匹配一种验证证据(日志/堆栈对齐、复现测试、源码路径追踪、环境对比、官方文档),证据不足时输出「最可能原因+待验证动作」,不得写成已确认根因;补丁式修复必须标注「补丁风险」并给出上游排查动作。
- [必须] 证伪门禁:关键算法/关键结论交付前构造反例尝试推翻——空值/空集合、边界值(0/1/最大/负数)、并发竞态、超大输入、自环互环(图/状态机算法);构造不出反例才允许交付。跑通 ≠ 正确,happy-path 通过 ≠ 无缺陷,禁止用弱用例通过冒充正确。
- [必须] jest 30 + ts-jest + 纯 ESM 依赖:parse-server/fmode-ng 为纯 ESM——jest 30 已移除
--experimental-vm-modules CLI 标志,须经 NODE_OPTIONS 传入(测试脚本 set NODE_OPTIONS=--experimental-vm-modules&& jest --runInBand);ts-jest(module commonjs)会把 import() 降级为 require(),动态加载 ESM 用 new Function('specifier','return import(specifier)') 原生动态导入绕过(生产与测试均可用)。
- [必须] Angular 20 测试三坑:①signal 版 NgModel 经 effect 异步初始化——首个稳定周期前派发的 input 事件全部无效(值被变更检测重置),必须先
detectChanges → whenStable → detectChanges 稳定一个周期再模拟输入;②expectOne 不幂等——同一 URL 第二次 expectOne 必失败,须保存第一次返回的 req 引用后 flush;③jest.mock 路径以文件真实位置为准(environments 常在 src/environments/ 而非 src/app/ 下),路径写错套件整体无法运行。
- [必须] 无头浏览器验收防挂起:headless=new 下
Page.setAutoAcceptDialogs 对 confirm 弹窗不生效,JS 对话框会阻塞渲染进程导致 CDP 调用永久挂起——双保险:页面注入 window.confirm = () => true + 监听 Page.javascriptDialogOpening 事件主动 handleJavaScriptDialog;所有 CDP send 必须带超时,挂起即快速失败而非无限等待。
建议规则
- [建议] 覆盖率报告:定期产出现有覆盖率报告,新模块测试先行(先写测试骨架再实现)。