原则与方法
三方式通用的纪律——不偏向任何一种,谁来跑都得守
速查
- 原则(守什么):一致性(确定可复现·环境隔离·不依赖自增 ID/时间)、独立性、反向验证、分级覆盖(安全类 100%)、回归网、bug-fail-first、精确到 case
- 方法(怎么做):TDD(红→绿→重构)、BDD(Given-When-Then)、五层模型 L1~L5、自测→报告→准入、提示词集工作流
- 关键立场:这些纪律手工、MCP、AI 写用例都要守——AI 没取消纪律,只是让纪律更便宜地落地
- AI 专属防线:AI 会写假绿用例(看着覆盖实则不验证),反向验证 + 人评审粒度是不能省的兜底
原则:三方式都要守的纪律
这些是测试的底线,跟你用手工、MCP 还是 AI 写用例无关——区别只在「怎么落地它」。
一致性:结果必须确定、可复现
同一个测试,今天跑、明天跑、在我机器跑、在 CI 跑,结论必须一样。做不到这点的「测试」是噪音。三条具体要求:
- 环境隔离:用独立的测试库 schema、独立缓存 db、独立配置 profile,绝不连开发库/生产库跑测试。凭据只放不提交 git 的本地配置。
- 不依赖自增 ID:测试数据的关键 ID 显式指定,别依赖数据库自增序列——换个环境就错位。
- 不依赖时间/随机:别让用例依赖「当前时间」「随机值」「数据库里恰好有几条」。要么固定,要么 mock。
手工怎么守:走查用固定的测试账号 + 共享 seed 数据。MCP 怎么守:让 AI 跑在测试环境、用固定账号。AI 写用例怎么守:spec 里数据自己造自己清,按 CODE/NAME/CREATOR 定位,禁 truncate 全表。
独立性:用例之间不许互相依赖
每个用例自带前置、自己清理,不靠「上一个用例跑完留下的数据」。用例 A 挂了不该连累 B;单独跑 B 也得能过。共享 seed 是只读基础数据,临时数据各用例自建自删,带可识别标记(特定 CREATOR/前缀)便于排查残留。
反向验证:证明用例真的在守护
这是我最看重的一条,也是 AI 时代最不能省的一条。覆盖率数字、绿色的测试,都可能是假的——
- 故意往被测逻辑打一个 bug → 对应用例必须 fail。
- 故意删一个安全类用例 → 安全类覆盖率必须从 100% 掉下来。
- 若打了 bug / 删了用例,指标纹丝不动 → 说明用例根本没在验证,或覆盖率统计范围配错了。
为什么 AI 时代尤其重要:AI 很擅长写「看着覆盖、实则不验证」的用例——
mount了组件但没断言、调了接口只验 200、expect(true).toBe(true)式的凑数。反向验证是揪出假绿用例的唯一可靠手段。
分级覆盖:安全类 100%,其余分级
不追求全局 100% 覆盖率(边际成本太高),而是分级:
| 类别 | 范围 | 行 / 分支门槛 |
|---|---|---|
| 安全类 | 认证登录、登录失败限制、验证码、token 校验、鉴权注解处理、数据权限、鉴权拦截器 | 100% / 100% 始终强制 |
| 业务核心 | 金额计算、状态机、权限派发、关键算法、核心领域服务 | ≥ 85% / ≥ 75% |
| 业务一般 | 常规 CRUD service / controller / 数据访问 | ≥ 70% / ≥ 60% |
| 框架/第三方/生成代码 | 框架原生、第三方库、生成器产物 | 不计 |
回归网:每个 bug 都进永久用例
任何缺陷修复,先有一个能复现它的 fail 用例,修完该用例转绿,永久保留进测试套件。同类问题再出现,回归网在自测阶段就拦掉,不再流到 QA。定期审查回归网健康度——有没有用例被长期 skip、有没有 flaky 用例被静默忽略。
bug-fail-first:先有复现用例,再修
跟回归网配套的铁律:禁裸修复。顺序是固定的——
- 先写复现用例,跑,确认它真的 fail(贴 fail 信息)。
- 再改代码(控制最小范围,不顺手重构)。
- 再跑,确认该用例 pass,其他用例不回归。
没有复现用例的「裸修复」无法证明 bug 真被修掉,也防不了复发,不许提交。
精确到 case:禁模块名粒度
测试计划必须精确到 case,禁止停在「新建 xxx 模块的 e2e」这种模块名粒度——这是历史漏覆盖事故的根因。每个 endpoint / 方法至少列三类 case:
- happy path:正常输入正常返回。
- 边界:空值、极值、临界、分页边界。
- 异常:非法输入、权限不足、依赖失败、并发冲突。
凡有副作用(写表、发消息、清缓存、触发流程、写审计、撤 token)的方法,逐副作用分支单独列 case,每分支至少 1 个。只写「该 service 覆盖率 ≥ N%」不算数。鉴权/数据权限/关键校验,必含「故意破坏 → 应 fail」的反例 case。
方法:怎么把纪律落地
TDD:红 → 绿 → 重构
先写测试再写实现,三步循环:
- 红:写一个 fail 的测试(功能还没实现,自然失败),跑,确认它真的 fail。
- 绿:写最小实现让它通过。
- 重构:在测试保护下整理代码,行为不变。
AI 时代的 TDD:提示词里显式要求「每个 case 先写 fail 测试 → 跑确认 fail → 再写实现 → 再跑确认 pass,禁止先写实现再补测试」。否则 AI 默认会先写实现、再补一个「正好能过」的测试,等于没 TDD。
BDD:Given-When-Then
用业务语言描述行为,结构是「给定前置 / 当触发动作 / 那么期望结果」:
Given 一个已登录的管理员
When 他提交一张超过预算余额的报销单
Then 系统拒绝并提示「预算不足」,且不写审计日志落到代码里,describe / it 的命名就照这个结构走——用例名直接说清「测什么、期望什么」,例如 doLogin_should_lockAccountAfter5Failures。禁内部代号(Phase3 / StageN / R1-1),半年后没人看得懂。
五层模型 L1~L5
测试分五层,每层职责清晰、互补,不重复测同一件事:
| 层 | 名称 | 工具示例 | 测什么 |
|---|---|---|---|
| L1 | 后端单元 | JUnit + Mockito | 纯逻辑,依赖全 mock,不启动应用/DB |
| L2 | 后端集成 | @SpringBootTest + 真 DB | 完整链路、数据权限、事务、鉴权注解真实行为 |
| L3 | 前端单元 | Vitest(不 mount) | 纯函数 / composable / store,后端 API mock |
| L4 | 前端组件 | Vitest + Vue Test Utils(mount) | 组件渲染 + 交互状态机 |
| L5 | 端到端 | Playwright / Cypress + 真后端 | 跨页面业务链路、真实鉴权、真实数据流 |
L3 与 L4 的唯一区分:是否
mount组件。纯函数/store 不 mount → L3;任何mount(Component)→ L4。比例上是金字塔:底层多而快、顶层少而慢,L5 占比最小(~5%)。L5 不计入覆盖率(它是「端到端正确性验证」,不是「代码行触达统计」),指标是通过率/flaky 率。
自测 → 报告 → 准入
研发自测产出《自测报告》,QA 准入审查通过才开测。自测报告必含:变更范围、分层测试结果(逐层用例数/通过数/命令/结果)、覆盖率(安全类单列 100%)、反向验证记录、已知未覆盖项(不得瞒报)、自测结论。「自测通过」四条缺一不可:应跑层全绿无 skip、覆盖率达分级门槛、至少做过一次反向验证、已知未覆盖项已显式列出。
提示词集工作流
把上面每类工作封装成可复制的 AI 提示词,串成一条流水线——这是「AI 写用例」能守住纪律的关键,也是这套方法论在 AI 时代的落地形态:
写 plan ──▶ ① 写测试用例计划(精确到 case)
│
▼ ⑥ 测试计划评审(粒度守门,第一次被退回是常态)
│
▼ ② 执行测试计划(TDD:先 fail 再实现,逐层推进)
│
▼ ③ 生成自测报告(跑五层 + 覆盖率 + 反向验证 + 结论)
│
▼ 提测
│
▼ ④ 测试准入审查(QA 视角:完整性核对 + 抽查复现)
│
▼ 测试 ──发现 bug──▶ ⑤ 缺陷修复(fail-test-first 铁律)
│
▼ ⑦ PR 测试 review(对应层齐全 / 真跑过 / 业务语义验证)横向辅助还有:⑧ 覆盖率审计 + 反向验证、⑨ Flaky/残留数据排查、⑩ 安全类清单维护,按需触发。每段提示词的尖括号占位项替换成实际值再投喂。关键是提示词 2(执行计划)显式写死 TDD「先 fail 再实现」——这是防 AI 偷懒先写实现的开关。
这套工作流不只服务「AI 写用例」
准入审查(④)、PR review(⑦)同样适用于手工和 MCP 产出的验证——它们守的是「提测质量」这道关,不挑测试是怎么来的。
下一步
- 手工·场景与阶段:这些纪律里「探索性」「主观判断」靠手工落地
- MCP·AI 跑 e2e·场景与阶段:MCP 怎么守一致性与环境隔离
- AI 写框架用例·场景与阶段:五层 + 提示词工作流的主战场,以及反向验证防假绿
- 参考:原则/方法清单 + 提示词工作流速查 + 五层速查