测试用例层
本 skill 只回答:AI 如何写高质量测试用例。不要写具体执行命令、端口、账号或工具步骤;这些交给 test-execution-router 与项目执行 skill。
0. 第一原则
在纯 AI 开发 loop 中,用例质量决定交付质量。测试用例必须先于施工冻结,作为施工后的验收真值;施工阶段不得因为代码跑不过而就地改断言。
[!CAUTION] 用例不再只是验收资产,它是施工规格——是承重墙。 结果管控模式下,冻结用例的
spec_hash是 goal 不得改写的施工锚;goal 只能产出候选件,最终完成由候选终审对真实断言与执行证据独立判定。用例写虚了,后面的网再多也只会验收一个虚目标(写虚/可对账的判据见 §2.1)。这一层松一寸,后面全线失守。 所以本 skill 有两条硬要求(§2):每条断言有稳定编号、每条断言可对账。
防作弊的不变量:脚本断言集合 ⊇ 规格断言集合。
脚本是规格的翻译——翻译可以修(写错了本来就该改),但不能删原文。因此:
| 用例规格(本 skill 产出) | 测试脚本(goal 产出) | |
|---|---|---|
| 谁写的 | 产品施工之外,经评审 | goal 的 M0 或正式验证阶段实现并修复 |
| 是什么 | 断言的定义 | 断言的翻译 |
| 能不能改 | 语义不可变(改了必须留痕) | 随便改,改错了本来就该改 |
| 哈希锚 | spec_hash ← 锚在这 |
没有,也不该有 |
不要去冻结测试脚本的哈希:脚本有 bug → 改了才能跑通 → 哈希变了 → 判定失败 → 死锁。锚必须锚在规格上,不是锚在翻译上。
每个重要功能至少同时考虑两条视角:
- 白盒链路用例:从程序链路、状态分支、数据不变量、异常边界推导。
- 黑盒业务用例:从用户视角、业务流程、权限/空态/错误态推导。
只写“测试新增接口是否正常”“验证页面能打开”属于空壳用例,必须重写。
1. 输入真值
按这个顺序收集来源:
| 来源 | 提供什么 |
|---|---|
| L1 需求 | 用户目标、业务链路、业务规则、不变量、异常边界 |
| L2 交互 | 页面状态、交互路径、视觉规格、空态/错误态 |
| L3 契约 | 请求/响应字段、错误码、鉴权、状态流 |
| L4 数据库 | 表结构、约束、索引语义、跨域 ID、终态不变量 |
| L5 客户端方案 | 状态管理、页面链路、缓存/重试/降级决策 |
| L6 服务端方案 | 状态机、事务边界、幂等、异步/补偿、外部依赖 |
| 轻量设计方案 | 决策表 DP-x + 完整规范条目索引 SD-x + 最终任务真值切片;用例必须覆盖所有可观察 SD,不能只覆盖 DP |
| 授权裁决记录 | _shared/用户裁决记录.md#DEC-x;证明死亡线/业务结果/契约与数据语义来自业务决策负责人,而非评审或测试层发明;兼容文件名不代表当前交互方自动有权 |
| diff | 本次真实改动面、风险面、回归范围 |
遇到上游不一致时,不要自行裁决;继续完成当前切片的有界只读检查,把真实决策缺口合并成一份裁决表。
例外——「设计没说」不是冲突,是缺陷:写断言时发现设计根本没定义这种情况下的结果,这不是测试层可以代答的问题。 处置:若正式真值能唯一推出答案,回填设计;若必须在多个业务结果之间选择,把本切片缺口合并给业务决策负责人裁决并生成
DEC-x后再回填。测试层与评审者都不得自行补出业务语义。 判据——「设计选错了」→ 那是翻案,设计已冻结,不许(走上游评审);「设计没说」→ 必须放行并回填。
2. 用例规格字段
每条用例必须包含:
| 字段 | 要求 |
|---|---|
case_id |
稳定、可引用;同一批内唯一。冻结后不得重排、不得复用——它是 change_log 与 execution_ref 的锚,改了就断链 |
case_type |
white_box / black_box / golden / contract / visual / manual(golden 的判定见 §2.2) |
design_refs |
对应 SD-x / DP-x;只用于证明设计传导,不是施工真值 |
provenance_refs |
涉及业务结果、死亡线、契约/数据语义或不可逆归属时必填 DEC-x 或上游正式真值锚点;普通机械用例可为空 |
formal_spec_refs |
正式 L1~L6 文件 + 小节锚点;用例设计阶段可先填已规划目标,规格冻结前必须全部可解析 |
business_goal |
用户或业务要被保护的结果 |
risk_guarded |
防什么回归、误解或质量风险 |
preconditions |
前置账号、权限、数据、环境状态 |
data_strategy |
DB 预置 / 接口造数 / 复用数据 / 人工前置 |
steps |
业务步骤,不写工具命令 |
expected |
可验证的业务结果 |
assertions |
逐条编号的断言清单——格式与硬要求见 §2.1 |
automation |
自动化类型或 manual_required |
env_fidelity |
保真度:real(真实链路)/ simulated(mock、构造回调、仿真数据等替身)/ manual(人工验证)。默认 real;凡用替身替代真实外部依赖的必须标 simulated,并注明真实链路在哪里收口(真机硬门 / 手工 runbook / 后续任务)——模拟绿冒充真连绿是交付阻断项 |
execution_ref |
目标测试文件/用例锚点;用例设计 / 规格冻结阶段可填预定锚点,goal 的 M0 负责实现为可运行落点,后续可按冻结语义修复 |
manual_reason |
仅手工用例填写:AI 无法操作的真实设备/原生对象/原生授权等物理边界证明;“视觉判断”“GUI”“交互式工具”不成立 |
manual_runbook |
仅手工用例填写:责任角色步骤、客观观测点、回传材料与计划内人工里程碑 |
2.1 断言:原子化 + 稳定编号 + 可对账
assertions 不是一段散文,是一张逐条编号的表。
| 字段 | 要求 |
|---|---|
id |
稳定编号 AC-x,在整份规格内唯一。冻结后永不复用、永不重排(删除的编号留空号,不许让给新断言) |
verifies |
这条断言验证的一个或多个 SD-x(可附 DP-x);不得填 —。多个 SD 只能共同支撑同一个可证伪结果;不同结果必须拆 AC |
provenance_refs |
断言改变或锁定业务结果/死亡线/契约与数据语义时必填 DEC-x 或上游正式真值;不得引用评审意见充当来源 |
formal_spec_refs |
承载该断言语义的正式规格锚点(docs/0N-.../文件.md#小节);用例设计阶段可先填已规划目标,规格冻结前必须全部可解析 |
assertion_kind |
business_result / contract / persistence / architecture / timing / concurrency / failure_isolation / visual / manual |
given |
可重建的初始状态;不得用“正常数据”之类模糊词 |
when |
唯一触发事件、入口与时点 |
then |
一个可独立证伪的结果;多个业务结果拆成多条 AC |
boundary |
平台、入口、分支、并发度、时间界点或失败边界;无特殊边界写 none |
required_test_shape |
能真实证明该语义的最低执行形状,见下表 |
2.1.1 证明部件(沿用 L7,不另建 observation contract)
每条 AC 默认有一个 result 证明部件:期望语义直接来自现有 then / boundary / required_test_shape,执行时必须绑定至少一个框架原生断言事件、真实扫描结果或采集时物理边界原始观测。AC 编号、测试名、套件退出码、文件存在和日志/聚合哈希都不是业务观测。
以下 AC 必须在同一 L7 内增加“证明部件覆盖表”,不得另建平行规格:死亡线/安全、concurrency、timing、failure_isolation、manual/真实外部链路,以及一个 AC 合法要求多个执行面或多个不可缺事实的情况。每行至少包含 AC / proof_part_id / surface / observable / expected_source / required_event_kind;expected_source 只指回该 AC 的 then 或正式规格锚点,不复制第二份业务答案。
多个 AC 合法消费同一个真实业务事实时,必须在 L7 声明等价组及正式语义依据;没有声明时,执行层不得用同一观测批量签发通过。普通 AC 不要求人工填写完整证明表,避免把轻量断言膨胀成第二套规格。
原子化硬规则:一条 AC-x = 一个可独立证伪的结果。平台、入口、快/慢路径、成功/失败分支、边界前/点/后、并发/串行是不同证明义务时必须拆开。一个 CASE-x 可编排多个 AC,禁止反过来用一个 AC 捆多个结果。
| 语义 | required_test_shape 最低要求 |
|---|---|
| 并发/幂等/单次决策 | 真并发竞争,禁止串行重放;同时断言终态与决策/派发次数 |
| 超时/失效/时间窗 | 固定或虚拟时钟,拆分边界前、边界点、边界后三个 AC |
| 快路径 + 回退路径 | 两路径各自取证,并证明最终决策/派发仅一次 |
| 批量/故障隔离 | 至少两项,其中一项失败而余项不受污染;另证明无逐项跨域/数据库调用 |
可对账的判据:
| ❌ 不合格(对不了账) | ✅ 合格(能对账) |
|---|---|
| 「应该失败」 | 「返回 400 / 错误码 40001 / retryable=false」 |
| 「返回成功」「data 非空」 | 「status=ACTIVE,amount=100.00,list 长度 3 且按 created_at 倒序」 |
| 「数据落库了」 | 「order 表新增 1 行,state=PAID;重跑同一请求行数仍为 1(幂等)」 |
| 「页面正常」 | 「显示空态文案『暂无数据』,且『新建』按钮可点」 |
判定试金石:两个人拿着这条断言分别去写脚本,会写出行为相同的断言吗? 不会 → 它写虚了,重写。
为什么必须编号(缺了它,防作弊三层防线全塌):
| 防线 | 依赖编号做什么 |
|---|---|
| 编号覆盖检查器 | 逐个编号去脚本里找落点——没有编号就没法机器判定"是不是漏测了/被 skip 了" |
change_log 追溯 |
有编号才能写「AC-7 从 X 改为 Y,因为…」;没编号只能写自然语言,追溯链在此断掉 |
| 红→绿翻转审计 | 有编号才能定位「哪条断言从红变绿、是改代码还是改脚本变的」 |
脚本侧标注格式(检查器的抓手,硬要求):每条 AC-x 编号必须以字面文本出现在实现它的测试资产里——测试名或紧邻注释均可。一条测试覆盖多个编号就列出多个编号。字面编号只证明“有落点”,不证明断言语义、测试形状或执行结果真正覆盖。 候选终审必须打开测试落点核对 given/when/then/boundary/required_test_shape;不得仅凭编号检查器放行。
2.2 golden(金标准)用例的判定与规格
定义落点说明:
test-standards负责何时必须有金标准(§2 高风险清单:鉴权 / 支付 / 用户数据删除 / 业务 ID / 核心算法 → 金标准必跑)与交付阻断条件(§5);金标准用例长什么样,由本节定义。
什么算金标准:保护业务不变量的用例——它验证的不是"这个功能能用",而是"这条业务规则永远成立"。
| 判定 | 说明 |
|---|---|
| 命中即为金标准 | 用例保护的是死亡线区域(清单以项目文档为准)的业务不变量 |
| 不是金标准 | 只验证某个功能的 happy path、某个字段透传、某个页面能打开 |
金标准用例的额外硬要求(普通用例之外再加五条):
- 不可删除:金标准是长期回归资产,功能废弃也要走显式裁决才能删
- 必须有
execution_ref:金标准若无execution_ref,视为「未生效」——等于没有 - 断言必须落到不变量本身,不是落到某次调用的返回值。例:不是「创建订单返回 200」,而是「任何路径下,
order.amount与order_item.amount之和恒等」 - 必须含至少一条反例断言:什么输入/时序会破坏这条不变量,系统必须挡住它
- 证据必须落到不变量与副作用:正向不变量终态和反例的负向副作用都要产生可区分的原生观测事件;套件通过、调用次数或报告哈希不能代替
死亡线区域的金标准用例是死亡线唯一的真锁。 不是更厚的设计文档、不是更多轮评审——是这几条能自动跑、跑挂就红的断言。
3. 白盒用例设计
白盒用例从系统内部风险推导,但断言仍要落到外部可观察结果或稳定不变量。
必须扫描:
- 关键分支:开关、状态、权限、空数据、异常输入。
- 状态机:合法转换、非法转换、重复请求、边界时间。
- 数据不变量:唯一约束、幂等、跨表终态、统计汇总。
- 事务与补偿:部分成功、外部依赖失败、重试。
- 算法与组装:边界值、缺失字段、格式转换、排序/分页。
- 并发与竞态:共享余额 / 限额 / 计数 / 唯一性资源的写路径,必须有真并发断言(并行执行下验证不变量,不是顺序重放)——顺序重放全绿、真并发才暴露不变量被破坏,是有实证记录的漏网形态。
白盒用例不等于“测私有方法”。只有当私有逻辑是保护业务不变量的唯一稳定入口时,才把内部锚点作为辅助定位。
4. 黑盒用例设计
黑盒用例从真实用户和业务路径推导。
必须覆盖:
- 正向闭环:用户完成核心目标。
- 反向路径:取消、失败、非法状态、权限不足。
- 空态/弱网/加载/错误态:用户能理解且可恢复。
- 多角色/多权限:未登录、普通用户、管理员、过期/无权限用户。
- 真实链路组合:接口、DB、UI、视觉按业务顺序连起来。
黑盒用例应避免“逐字段罗列”。它要说明用户做了什么、系统应给出什么业务结果。
5. 用例冻结
唯一正式副本规则:用例设计阶段直接把完整用例写入项目规定的正式 L7 路径;任务 _shared/ 只保存路径、摘要和哈希,不保留第二份完整用例。可先写待冻结稿,规格冻结阶段在全部 formal_spec_refs 可解析且物化检查全绿后完成最终冻结。
用例规格冻结头部必须包含:
frozen_at: <冻结日期>
frozen_by: <用户或任务标识>
design_refs: [<SD-x/DP-x>]
formal_spec_refs: [<正式 L1~L6 文件#小节>]
spec_hash: sha256:<冻结时规范化全文哈希> # 粗锚:判"有没有被动过"
assertion_index: [AC-1, AC-2, ..., AC-n] # 细锚:判"哪一条被动了"
change_log: []
spec_hash 复算规范:把规格全文中唯一的 spec_hash: 行替换为 spec_hash: SPEC_HASH_PLACEHOLDER(保留换行),再对 UTF-8 全文计算 SHA-256。这样哈希不自引用,机器可稳定复验;缺行或多行均不得冻结。
为什么要两个锚:spec_hash 是全文粒度——改个错别字也翻 hash,它只能回答"动没动过",回答不了"动了哪条"。assertion_index 冻结编号全集,让 §2.1 那三件事(机器判漏测 / change_log 点名 / 红→绿翻转定位)成为可能。
冻结后规则:
goal 内失败:产品实现不符则修产品;测试资产/runner/夹具/环境适配/证据工具缺陷也在同一 goal 修。任何路径都不得改弱用例断言,修复后按影响面重跑测试与证据。
职责路由:可逆工程缺陷归 goal;用例语义缺陷退回本层;正式规格/业务语义缺陷退回设计与规格冻结。失效范围按影响面计算,禁止一个 runner 小错作废整条上游链。
不得削弱断言:脚本的断言集合只能 ⊇ 规格。改脚本时的唯一判据——改完之后,脚本是更接近规格,还是更接近代码?
场景 规格说 代码实际 脚本原来 改成 判定 A 400 400 404(写错了) 400 ✅ 忠实化 B 400 404(有 bug) 400 404 ❌ 作弊 规格是唯一的裁判席。 规格一旦可变,A 和 B 就再也分不开了——可以先改规格再理直气壮地改脚本。这就是冻结必须存在于施工之外的理由。
认为用例规格本身错了 → 不许就地改。带证据停机附修改方案(改哪些设计/用例、影响面、恢复点),业务决策负责人按项目治理批准后修订用例与上游、按影响面重跑并断点续跑(默认);热修不可靠且获相应批准才回炉重跑(见
goal-charter§4/§5)。改规格让测试变绿 = 任务失败,不是聪明。冻结后任何断言变化都必须写入
change_log,逐条点名编号(改了哪个AC-x、从什么变成什么、依据哪份冻结真值)。spec_hash改变但change_log没有记录,视为违规改判。AC-x编号永不复用:删除的断言留空号。复用编号会让change_log与execution_ref指向错误的历史。
5.1 用例评审只开放一次
同一阶段、同一真值基线、同一用例对象只允许一次开放式对抗评审。回补后必须走 closed-remediation-review:本工具一个原生子线程检查原采纳项、DEC-x 与整改 diff,主线程逐条裁决。禁止通过 R2/R3 继续发明边界用例;若整改导致根本性重设计,先登记旧评审失效原因,再按新对象处理。
6. 质量审查清单
用例提交前逐项自检:
- 每条断言是否有稳定编号
AC-x,且assertion_index已冻结。(缺编号 → 三层防作弊防线全塌,见 §2.1) - 每条 AC 是否只含一个可独立证伪结果;是否已拆开平台、入口、分支、时间边界与并发证明义务。
- 每条 AC 的
assertion_kind / given / when / then / boundary / required_test_shape是否齐全。 - 每条 AC 是否至少有默认
result证明义务;复杂/高风险 AC 是否在同一 L7 内拆清全部证明部件,且没有另造平行 observation contract。 - 每条断言是否可对账:拿它给两个人分别写脚本,会写出行为相同的断言吗?不会 → 重写。
- 每条高风险断言是否有决策来源:涉及业务结果、死亡线、契约/数据语义却无
DEC-x或上游正式真值 → 失败;写“已批准”但找不到原始证据与实际授权角色 → 失败。 - 测试是否越权造规则:AC 新增了上游不存在的状态分支、错误码、时点口径或数据语义 → 失败,退回设计/业务决策负责人裁决,不得冻结。
- 每个
SD-x是否至少被一条AC-x覆盖:包括负面约束与架构约束;可用静态检查器断言,不能裸 N/A。DP 由其所属 SD 间接闭合。 - 每个
SD-x是否已物化:必须有正式规格落点;AC 同时引用design_refs与formal_spec_refs,不能只追轻量设计。 - 每条 AC 的
formal_spec_refs是否指向真实存在的正式 L1~L6 小节;只引用轻量设计、任务总控或代码均判失败。 - 死亡线区域是否有
golden用例,且都带execution_ref(金标准无execution_ref= 未生效)。 - 每个关键功能是否至少有白盒和黑盒视角。
- 每条用例是否能追溯到 L1~L6 或明确的 diff 风险。
- 保真度是否标注:用替身(mock / 构造回调 / 仿真数据)的用例是否标了
simulated且写明真实链路收口去向。 - 共享资源写路径是否有真并发断言(余额 / 限额 / 计数 / 唯一约束类)。
- 断言是否验证业务语义,而不只是成功码/非空/存在。
- 写链路是否有 DB 终态或不变量校验。
- UI 路径是否覆盖关键状态反馈。
- 视觉变化是否有判读点。
- 手工用例是否只用于 AI 无法替代的物理边界,且物理证明、runbook、回传材料与人工里程碑责任角色齐全;AI 可控 GUI/VLM 不得标手工。
execution_ref是否在设计阶段先占位,并明确由 goal 的 M0 实现/健康检查,而不是终审时反向发明。
7. 输出格式
# 测试用例规格
## 冻结信息
frozen_at:
frozen_by:
design_refs:
formal_spec_refs:
spec_hash:
assertion_index:
change_log:
## 用例矩阵
| case_id | type | design_refs | provenance_refs | formal_spec_refs | business_goal | risk_guarded | data_strategy | automation | env_fidelity | execution_ref |
## 用例详情
### CASE-001
- 视角:
- 前置:
- 步骤:
- 期望:
- 断言(逐条编号,见 §2.1):
| id | verifies | provenance_refs | formal_spec_refs | assertion_kind | given | when | then | boundary | required_test_shape |
|---|---|---|---|---|---|---|---|---|---|
| AC-1 | SD-3 / DP-3 | DEC-1 | `docs/03-...#错误语义` | contract | 请求缺必填项 | 提交请求 | 响应错误码 40001 | C 端接口 | 真实契约测试 |
| AC-2 | SD-3 / DP-3 | DEC-1 | `docs/03-...#错误语义` | contract | 请求缺必填项 | 提交请求 | `retryable=false` | C 端接口 | 真实契约测试 |
- 不可自动化原因:
8. 与其他 skill 的边界
- 判断要测哪些类型:使用
test-standards。 - 执行矩阵和工具路由:使用
test-execution-router。 - 项目内具体测试脚本写法:使用项目执行 skill。
- 测试资产/runner 的实现、健康检查与修复:由
test-execution-router的bootstrap/verification模式在同一 goal 内路由到项目执行 skill。