better-test · git:20261008.3911d61 · 2026-10-08 · sha256 7b82d9730b4a5f41

better-test git:20261008.3911d61A

Immutable. This exact content never changes and is served at /api/v1/blob/7b82d9730b4a5f41.

---
name: better-test
description: 写、改、审查或清理测试时使用:价值门、反模式、见红、接缝与 mock 边界、DELETE/REWRITE/KEEP 判决,以及自动化够不到时的取证。
---

# Better Test

测试是负债:每条持久测试都必须在失败时说明某个真实行为坏了。覆盖率、测试数量、"每个函数一条"都不是目标。

只走一个分支:

1. 写或改测试 → **写测试**。
2. 审计或清理既有测试 → **清理与审计**。
3. 修已知 bug → 先在 bug 真实发生的接缝上写出复现用户准确症状的失败用例,再修;这条回归用例按**写测试**过价值门。
4. 行为值得保护但自动化碰不到 → **取证**。

## 价值门

写新测试与判既有测试共用这一道门,六条必须全部成立:

1. **有来源**:保护的行为有独立依据——需求、bug、公开合同、可访问性规则或规格示例。
2. **失败有意义**:一种可信回归会让它失败,且失败对应调用方或用户可见的问题。
3. **期望值独立**:期望值来自已知字面值、规格或示例,有可能与实现结果不一致。
4. **稳定接缝**:经公开接口或稳定 seam 观察,不依赖只为测试存在的生产导出、flag、wrapper 或注入点。
5. **抗重构**:行为不变的内部重构、无关文案或布局调整后仍然通过。
6. **唯一 owner**:该合同只在最低的可靠边界证明一次,且没有被现有测试、类型系统、编译器或 lint 等价保证。

**接缝**是不深入内部就能观察行为的公共边界。纯规则放领域测试,编排与授权结果放应用层,交互合同放组件测试,端到端只留关键用户流程。示例见 [references/tests.md](./references/tests.md),mock 边界见 [references/mocking.md](./references/mocking.md)。

## 反模式

判断时按类对照;正则或文本命中只是线索,读完测试和被测代码才下结论。

- **同义反复**:按实现的算法重算期望值;断言声明等于自身;mock 返回什么就断言什么;期望值由被测 helper 或 renderer 自己产出。
- **变更探测器**:扫源码、import、标识符或字符串;数导出、路由、元素或清单行数;快照生成的 markup、组件树或 class 串;断言私有协作者、调用次数或调用形状。
- **重复**:同一场景在它穿过的每一层都重放一遍;近乎相同的用例(合并成表驱动);已被类型或编译保证的检查。
- **空洞**:没有断言或只证明"没崩";只测 getter、常量或框架能渲染。
- **虚假信号**:负向对照因无关原因通过(另一个守卫先拒绝、生产路径根本到不了);fixture 替被测方产出本该由它产生的结果;测试名承诺的比输入实际覆盖的多。
- **测试专用生产接缝**:只有测试调用的导出、全局、wrapper、flag,以及只被测试调用的死代码。
- **外观当行为**:尺寸、比例、坐标、computed style、布局 class、`scrollWidth`、挂载行数是外观证据,不是行为。视口只提供场景,断言落在用户任务完成上(移动端能找到并操作某个按钮)。用 role、可访问名、label 定位控件;只有措辞本身是需求时才断言文案。焦点和禁用态只在它就是交互合同时测(聚焦首个无效字段、对话框关闭后焦点归还);归一化矩阵放纯函数测试,组件层留一条代表用例。截图断言只在项目明确采用视觉回归时入套件。

**像实现但必须保留**:公开 API、协议与序列化字节、配置、迁移、存储格式、安全、默认值、跨语言生成物、架构约束——它们本身就是独立合同,满足价值门第 1 条。顺序可被观察时的调用顺序、以最低成本守住用户可见键名或路径的源码检查同理。

## 写测试

1. **先决定要不要写**:纯重构、改名、格式化等无行为变化 → 只跑受影响的既有验证。已有用例覆盖该行为 → 改它,不新增。新行为 → 过价值门,不过就不写,也不记测试债。
2. **放在 owner 边界**:同一行为的多处改动共用一条用例;优先扩展表驱动用例或共享 fixture。需要测试专用生产接缝时,换到真实边界上测。
3. **见红**:每条新测试都要亲眼看到它因预期原因失败一次。验收测试(implement)和 bug 回归测试天然先红;为既有行为补的测试,临时破坏被测实现(不是改断言),运行确认变红,再精确还原并跑绿。从没红过的测试只证明了 mock。
4. **写法**:一条用例一个行为,失败信息能直接定位合同;固定 seed、时钟和网络,用条件等待代替 sleep;不留永久 skip、临时断言、僵尸进程或生成物。
5. **顺手清理**:本次触及的测试文件及同一行为的相邻用例,按下文判决规则直接处理,不另请授权。
6. **验证**:跑最窄受影响验证。

完成标准:每条新增或保留的触及用例都过价值门且见过红;报告里有命令、结果,以及删除或拒绝新增的理由。

## 清理与审计

**范围与授权**:范围按用户请求钉死——diff、路径、模块或全仓;全仓只在用户明确要求时做。"审计""看看""找找"是只读,交报告为止;"清理""删掉""精简"授权在范围内直接改。范围跨 3 个以上模块时按 [fan-out](../fan-out/SKILL.md) 中档分工;全仓清理或用户要求仔细时用高档:DELETE 判决另派一个不同模型的子代理独立复判,两份一致才删,不一致的记为未定。测试专用的切片与两波流程见 [FANOUT.md](./FANOUT.md)。

**逐条判决**,不按文件。判之前读完测试全文、被测生产 owner、它的调用方、重叠测试、相关 git 历史,以及项目自己的测试规则。

- **KEEP**:价值门六条逐条举证全部成立。
- **REWRITE**:测试里有一个过门的合同,但现写法不过。只保留证明该合同所需的准备、动作和断言,不沿用旧测试的体量、断言数或层级。
- **DELETE**:不过门,且写全三项证据——它实际能抓住什么失败;该行为别处更强的证明,或为什么不需要证明;它或它依赖的测试专用接缝当初为何存在。
- **未定**:KEEP 举证不全,DELETE 证据也不全。写进报告,不动。

既有测试在基线上就失败时,当作可能的产品缺陷先定位根因,不删。

**执行**:删测试时一并删掉它独占的测试专用生产接缝与死代码,以及因此失效的 fixture、helper、快照和 setup。为保住测试而改生产行为、降低断言、关闭规则、调低覆盖率阈值都不是解法;覆盖率或数量门槛挡住合理删除时,报告冲突。改完跑最窄受影响验证和仓库要求的门。

**报告**:写明是审计建议还是已完成的清理。逐条列出 DELETE / REWRITE / KEEP / 未定 的位置与理由;KEEP 和 REWRITE 附六条举证,DELETE 附三项证据;列出删掉的生产接缝、跑过的命令与结果,生产代码与测试代码的行数变化分开写。

完成标准:范围内每个候选都有判决,留下的测试全部过门,没有残留的死测试支撑代码,验证结果已知。

## 取证

按接近用户真实路径的顺序选最短方法:

1. **better-browser-use**:页面、DOM、截图、console、network;
2. curl 或 CLI:直接调用并断言响应内容;
3. 带具体断言的日志;
4. **better-computer-use**:桌面应用或真机兜底,读取控件并截图;
5. 都不可行:给用户"打开哪里 → 做什么 → 应看到什么"的最短清单,标注未验证。

证据必须来自本次执行。只有过价值门、值得长期保护的缺口才记测试债;同一路径第二次需要人工取证时,向用户提出补一个自动化验证入口。