功能测试用例 → 分层自动化单测
把人工执行的功能用例变成自动化单测。断言只来自功能用例;实现和用例不一致时测试失败,失败原因写进报告,交给开发改实现。
本 skill 的核心是防止测试把自己改绿。规则集中在下面「四条硬约束」,正文其他地方只引用不重述。
适用与不适用
| 适用 | 不适用 |
|---|---|
| 用例已定稿(md 或 csv)、每条有稳定 TC 号 | 用例还没写 → testcase-design |
| 被测代码已存在,要补自动化 | 要评审用例本身写得好不好 → testcase-closed-loop |
| 要核对「断言 ↔ 判据」是否一致 | 只要把定稿用例转 Excel |
| 要建立 TC 号到测试的追溯门禁 | 探索性测试、性能测试 |
本 skill 与语言、框架、业务域无关。 正文只讲流程与约束;框架特有的写法(标记、断言 API、mock 姿势、构建命令)全在技术栈附录。当前有 Java/Maven 一份;碰到新栈按阶段 0 探测,并把结论沉淀成同格式的新附录。
四条硬约束
- 红的原因是「判据与实现不符」时,禁止改断言让它变绿,禁止改开发实现代码,禁止用任何标记(如旧规则的
deviation)或分组把失败排除出报告。 断言保持用例要求,测试保持失败,失败原因写入报告。这是防止评审循环退化成「改到绿为止」的唯一保险。 - 判据文件对实现侧只读。 认为判据错了就停下来提问,不许改判据。功能用例本身可以在过程中被测试同学更新,但只能走「重新入库」换基线,不能由写单测的人直接改副本。业务已拍板的口径可以写进用例,仍然不许为此改开发代码。
- 权威源顺序:定稿的用例原文(入了需求管理系统的,以系统里的为准)> PRD / 需求原文 > 仓库内开发侧整理的需求文档。禁止拿开发侧文档判人工用例「写错了」——视角不同,会大量产生假阳性。
- 门禁在命令的非零退出码,不在「我跑过了」。 判断测试是否真执行只看条数(
Tests run: N/N passed),不看构建是否成功:过滤器匹配不到某些测试结构时会报 0 条且构建成功,这是最危险的假绿。
固定词汇
每次执行都用这套词。分层按「被测对象的依赖半径」切,半径越小越先做。
| 层 | 依赖 | 测什么 |
|---|---|---|
| 纯口径层 | 无(纯函数) | 公式、精度、量纲、舍入、尾差、取整、聚合口径、判定真值表 |
| 规则层 | Mock 框架,不起容器、不连库 | 拦截门槛、状态流转、幂等、校验顺序、提示文案、交互调用 |
| 集成层 | 真库 / 真接口 | 跨表一致性、SQL 聚合、导入导出往返、端到端 |
| 人工层 | 浏览器 | 页面跳转与展示、控件形态、颜色、置灰、Toast、下载中心 |
本 skill 只产出前两层的代码。列出后两层是为了让阶段 1/2 能把用例归类并登记为缺口——判定「这条不属于单测范围」本身就是产出。不写集成测试脚本、不写 UI 自动化。
标记(可叠加,门禁用「包含 + 排除」组合):
| Tag | 含义 |
|---|---|
pure |
零 mock 纯函数 |
case |
判据来自用例文档 |
impl |
判据来自代码实现,用例里没写。不得用来覆盖用例已写明的判据:一条 TC 只被 impl 方法引用、没有一个 case 断言,追溯脚本第 6 项判门禁失败 |
用例定性四态:可自动化 / 仅人工 / 无实现-阻塞 / 判据未定-阻塞。
流程
复制这份清单并逐项推进:
- [ ] 用例入库:拿到路径,复制到 doc/testcases,核对文件与 TC 条数
- [ ] 阶段0 探测技术栈与基线
- [ ] 阶段1 定性:用例 ↔ 代码落点映射
- [ ] 阶段2 拆解:覆盖矩阵 + 分层 + Tag
- [ ] 阶段3 实现:先验证脚手架,再铺开
- [ ] 阶段4 独立评审(循环 ≤ 3 轮)
- [ ] 阶段5 双清单 + 单测报告
每阶段的输入、交付物模板、可判定的完成标准与回退路径见 stage-contracts.md。完成标准是硬门槛,不满足不得进入下一阶段。入库与阶段 0/1/2 的产物合并落盘为 doc/testcases/{需求名称}-自动化覆盖矩阵.md(用例来源 / 环境基线 / 四态清单 / 矩阵四节)。增量执行(已有部分测试、只补一批)时哪些结论能复用见该文件末节。
用例入库
先收用例,再进入阶段 1、2。 阶段 0 的环境探测可以并行。
- 消息里没有功能测试用例的路径时先问用户,不要自己去仓库外找,也不要先读实现再拆用例。
- 只把
{需求名称}-测试用例.md/.csv和同目录的评审指引复制到项目doc/testcases/;work/、_archive等过程稿不复制。md 与 csv 都有时先问以哪份为准。文件已在doc/testcases/时直接用,不再拷一份。 - 复制后列出文件名和每份的 TC 条数;多份需求时问从哪一份开始。
- 记下每份用例的源路径和入库时的 sha256(源文件与副本各一份;文件本来就在
doc/testcases/的只记副本),写进覆盖矩阵文件的「用例来源」表。这是本次执行的判据基线:副本是新文件,git status 看不出它被改过,评审方和阶段 5 都拿副本重算 hash 与这条基线比。
基线只锁本次执行,不冻结功能用例。测试同学在过程中更新了用例,走重新入库:从源重新复制、在「用例来源」表追加一行新基线、受影响的 TC 回到阶段 1 重新定性。没有走重新入库而副本变了,就是判据被改。
用例文件怎么读(编号 / 前置 / 判据明细行的位置、范围声明、评审指引、CSV 验证维度如何归层)见 reading-testcases.md。
阶段 0:探测技术栈与基线
查明并记录语言版本、测试框架与版本、mock 框架与版本、ORM 及其链式 API 的 mock 姿势、构建工具测试插件版本、测试引擎是否齐全(缺引擎会静默不执行)。然后跑一次基线,记下测试总数的具体数字(硬约束 4)。
探测结果写成「脚手架约定」。Java / Maven / JUnit5 / Mockito / MyBatis Plus 见 java-maven-pitfalls.md;其他栈按该文件格式新建 references/<栈名>-pitfalls.md。
阶段 1:定性 —— 用例 ↔ 代码落点映射
这是全流程最省事的一步,跳过它后面全是返工。 对每条 TC:
- 找代码落点:定位到具体的 public 方法与行号。找不到就是
无实现-阻塞,用全量 grep 证明(如整个模块 grep 某业务词 = 0 命中),不凭印象。 - 逐行查判据是否已定:含【待确认】/【待拍板】/「以实际为准」的明细行判据未定。标题带【待确认】或全部明细行未定 → 整条
判据未定-阻塞;只有部分行未定 → 整条照常定性,未定行阶段 2 不计入可自动化行,登记到清单 2「待业务拍板」。不要因为一行没定就拦掉整条。
无实现-阻塞 的用例现在跑一定全红,那是「功能没做」不是「功能做错」,报告里单列,不当 bug 提。
阶段 2:拆解 —— 覆盖矩阵
给每条 可自动化 的 TC 定层、定 Tag、定落点(到 类#方法),产出机器可核对的覆盖矩阵,字段口径与模板见 stage-contracts.md 阶段 2。
「明细行」每一行都是判据,都要有人认领。「明细行 − 可自动化行」的每一行在备注里点名原因,只有四类:人工层(页面表现)/ 集成层(判据要靠协作方真实行为才成立,而协作方要真库、真接口,或在本仓库没有实现)/ 判据未定 / 本层原理上验不了(如链式 mock 吞掉查询条件)。一行同时符合几类记「判据未定」——拍板后它可能就能写了。
不写断言的理由要像「无实现-阻塞」一样给证据,否则「把难写的行定性掉」就是最省事的绿法。备注里每条原因都要附可核对的依据:人工层附判据原文里的页面用词;集成层附协作方的类#方法和它为什么在本层拿不到真实行为;验不了附具体的 mock 姿势和被吞掉的条件;判据未定附【待确认】原文。只写类别名不写依据的行,按可自动化行处理。
预期方法数总和写出来,这是阶段 3 的验收基准。矩阵必须落盘,不能只在对话里说。
阶段 3:实现
先脚手架 → 用一个靶子用例验证脚手架 → 再铺开,顺序不能颠倒。
- 共享脚手架:mock 搭建、框架初始化、校验器执行收进一个工具类。纯口径层的测试不得继承它,否则失去「跑得快、不会因打桩写错而假绿」的唯一优势。
- 靶子验证:挑一条「判据最明确 + 资金/正确性风险最高 + 零环境依赖」的用例先写、跑绿。这一步暴露脚手架的全部问题。
- 铺开:按矩阵逐个落点写,每个测试类写完立刻跑。
引用约定:测试类注释、用例名注解、断言说明三处都写 <用例文件>.md#TC-0008 这种带文件名的回指;TC 号只用文档里写的那个;方法级标记自带 文件名#编号。原因与反例见 traceability.md。
断言强度按 assertion-rules.md 的十条逐方法对照,核心一句话:对每个测试方法回答「怎样改坏生产码它仍绿」,答得出来就是漏洞。评审方按同一份规则反查。
每轮实现完跑全量,核对测试方法数 = 矩阵预期方法数之和,并做加减:全量 = 判据门禁(含失败)+ 带 impl 标记的测试(纯 impl 与 case+impl 叠加都算)。
阶段 4:独立评审(循环 ≤ 3 轮)
必须派独立上下文的子 agent 评审,不能自查——写的时候就是照着实现写的,自查抓不出「断言跟着实现走」。实现仍由主 agent 完成(它已有全部上下文);评审子 agent 只回传文本,文件写入由主 agent 做。子 agent 类型:
- 有评审类子 agent(如
CodeReview)→ 用它 - 只有通用类型(Cursor
generalPurpose、Claude Codegeneral-purpose)→ 用它,prompt 写明「不得修改任何文件」 - 没有子 agent → 停下告诉用户,请用户另开新会话按 review-dimensions.md 的模板评审;不得用主 agent 自查顶替
派发 prompt 里显式给出:被测代码、测试代码、判据事实源、覆盖矩阵的绝对路径,10 条评审维度,输出格式,并要求评审方自己跑一遍测试、按 Tag 分组核对条数。不依赖 git diff。维度与 prompt 模板见 review-dimensions.md。
路径清单是主 agent 填的,少给一个文件评审方就看不到。所以 prompt 里要写明:给出的路径是起点不是边界,评审方必须从测试代码的 import 反查全部被测生产类(以及这些类直接调用的本仓库类),从矩阵反查全部用例文件,并在报告里列出主 agent 没给的文件。prompt 里还要给用例的入库 hash,让评审方自己算副本 hash 与它比对——判据有没有被改,不能只由被评审方自证。比的是入库基线,不是当前源文件:源文件比入库时新了,是「用例有更新、需要重新入库」,不是违规。
评审方回传的文本由主 agent 原样追加到 doc/testcases/{需求名称}-评审记录.md(每轮一节,不得改写或删节),清单 1 从这里摘。评审原文不落盘,🔴 就可以在转录时消失。
评审 → 有 🔴/🟠 → 主 agent 修 → 复评 → …… 最多 3 轮
3 轮后仍有 🔴 → 停止,把未解决项写进交付清单并明确告知用户,不得静默收尾
循环的终止条件只有「评审通过」或「用满 3 轮」,不是「测试变绿」。因判据与实现不符而红的测试按硬约束 1 处理,失败写进单测报告第 6 节。
阶段 5:双清单 + 单测报告
评审的价值一半在测试质量,另一半在挖出判据与实现的差异,两份清单都要交。
- 清单 1 · 测试缺陷清单(给写测试的人,改到评审通过):按 🔴 / 🟠 / 🟡 分级,每条给「文件:行号 + TC 号 + 判据原文 + 断言摘录 + 结论」。
- 清单 2 · 失败定位清单(给测试定位、给开发改实现,不许改绿):三节——当前失败 / 待业务拍板(拍板前不写断言、不进门禁)/ 已知覆盖缺口。写进用例目录的说明文件,测试注释引用它时写「失败原因见某文件」,引用不悬空。
- 单测报告
doc/testcases/{需求名称}-单测报告.md,七节缺一不可,条数从命令输出里抄。结论只有两档:通过(全量 0 失败 0 错误 0 跳过、追溯退出码 0 且未覆盖编号为 0、矩阵无待写、副本 hash 与入库基线一致、无未解决 🔴、无待拍板)或 未完成(其余任一情况)。有失败就写未完成,第 6 节逐条写到测试人员能找到用例、测试方法和开发代码。「难写的 TC 留着不写」不能换来通过:矩阵里每条可自动化要么已写,要么不覆盖(原因)并经评审复核。 - 第 4 节用例覆盖之后紧接着写代码覆盖率:只统计当前分支相对基线分支(默认
origin/master)里本需求改过的可执行行,主数字是行覆盖率,按类列未覆盖方法;大段为 0 要写原因;不许为了百分比改代码、改断言或补用例明确不测的测试。
清单与报告的模板、覆盖率口径细则见 stage-contracts.md 阶段 5;Java/Maven 的覆盖率采集与 diff 求交见 java-maven-pitfalls.md。
交付前三件事:重新算 doc/testcases/ 里用例副本的 sha256 与入库基线比对,不一致 = 本次执行中判据被改,结论只能是未完成;顺带看一眼源文件是否比入库时新了,新了就在报告里写明「本报告针对入库版本,源已更新,待重新入库」,由用户决定是否再跑一轮;跑门禁命令(全量 / 纯口径 / 判据门禁)做加减核对;跑追溯校验:
python <skill目录>/scripts/check_tc_refs.py --docs doc/testcases --tests <测试源码目录> --matrix <覆盖矩阵>
命令表、加减核对公式、脚本六项检查与跨语言参数见 traceability.md。回复里给结论和报告路径,报告全文留在文件里。
产物清单
| 产物 | 位置 |
|---|---|
| 共享脚手架 | 测试源码树的公共目录下 |
| 分层测试代码 | 与被测类镜像的目录结构下 |
| 覆盖矩阵(用例来源 + 环境基线 + 四态清单 + 矩阵,四节合一) | doc/testcases/{需求名称}-自动化覆盖矩阵.md |
| 评审记录(每轮评审原文,只追加不改写) | doc/testcases/{需求名称}-评审记录.md |
| 失败定位清单 | 用例目录说明文件的独立小节,并抄进单测报告第 6 节 |
| 单测报告(七节) | doc/testcases/{需求名称}-单测报告.md |
| 追溯校验结果 | 脚本输出,进 CI;摘要写进单测报告第 7 节 |
参考资料
- 阶段契约(输入 / 交付物模板 / 完成标准 / 回退 / 增量执行):stage-contracts.md
- 读用例(两种产物格式、判据认定、按行归层):reading-testcases.md
- 断言规则(十条,每条标明防的是哪种假绿):assertion-rules.md
- 引用约定、门禁命令、追溯脚本六项检查:traceability.md
- 评审维度(10 条)与子 agent prompt 模板:review-dimensions.md
- 技术栈附录:java-maven-pitfalls.md;其他栈现场探测后新建
references/<栈名>-pitfalls.md - TC 引用反向校验脚本:
scripts/check_tc_refs.py