Skip to content

原则与方法

三方式通用的纪律——不偏向任何一种,谁来跑都得守

速查

  • 原则(守什么):一致性(确定可复现·环境隔离·不依赖自增 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:先有复现用例,再修

跟回归网配套的铁律:禁裸修复。顺序是固定的——

  1. 先写复现用例,跑,确认它真的 fail(贴 fail 信息)。
  2. 再改代码(控制最小范围,不顺手重构)。
  3. 再跑,确认该用例 pass,其他用例不回归。

没有复现用例的「裸修复」无法证明 bug 真被修掉,也防不了复发,不许提交。

精确到 case:禁模块名粒度

测试计划必须精确到 case,禁止停在「新建 xxx 模块的 e2e」这种模块名粒度——这是历史漏覆盖事故的根因。每个 endpoint / 方法至少列三类 case:

  • happy path:正常输入正常返回。
  • 边界:空值、极值、临界、分页边界。
  • 异常:非法输入、权限不足、依赖失败、并发冲突。

凡有副作用(写表、发消息、清缓存、触发流程、写审计、撤 token)的方法,逐副作用分支单独列 case,每分支至少 1 个。只写「该 service 覆盖率 ≥ N%」不算数。鉴权/数据权限/关键校验,必含「故意破坏 → 应 fail」的反例 case。

方法:怎么把纪律落地

TDD:红 → 绿 → 重构

先写测试再写实现,三步循环:

  1. :写一个 fail 的测试(功能还没实现,自然失败),跑,确认它真的 fail。
  2. 绿:写最小实现让它通过。
  3. 重构:在测试保护下整理代码,行为不变。

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 产出的验证——它们守的是「提测质量」这道关,不挑测试是怎么来的。

下一步