---
name: better-test
description: 验证改动、写测试、审查或清理测试时使用：验证方法、什么测试值得留、端到端测试写法、DELETE/REWRITE/KEEP 判决。
---

# Better Test

测试是负债。agent 自己写的单元、集成测试只复述实现：测试和代码出自同一份理解，不比实现更准；实测不提高正确率，只增加时间和费用。持久测试只有两种来源：用户给出或认可的用例，和覆盖关键用户流程的端到端测试。

只走一个分支：

1. 验证一次改动 → **验证**。
2. 用户要求测试，或工单给了用例 → **写测试**。
3. 审计或清理既有测试 → **清理与审计**。

## 验证

按接近用户真实路径的顺序选最短方法，证据来自本次执行：

1. 项目已有测试、类型检查、构建中受影响的部分，或直接运行改动的命令；
2. **better-browser-use**：页面、DOM、截图、console、network；
3. curl 或 CLI：直接调用并核对响应内容；
4. **better-computer-use**：桌面应用或真机；
5. 都不可行：给用户"打开哪里 → 做什么 → 应看到什么"的最短清单，标注未验证。

修 bug 先按用户描述实际复现症状，修完走同一路径确认消失。同一路径第二次需要人工取证时，向用户提议补一条端到端测试。

## 价值门

判既有测试和写新测试共用这一道门，六条全部成立才留：

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


## 反模式

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

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

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

## 写测试

1. 用例先与用户对齐；工单或用户给的用例原样采用。
2. 写成端到端测试：从用户真实入口（页面、CLI、公开 API）完成任务并断言结果；只 mock 付费或不可控的外部服务。
3. **见红**：每条新测试亲眼看到它因预期原因失败一次——功能尚未实现，或临时破坏被测实现（不是改断言）后再精确还原。从没红过的测试只证明了 mock。
4. 一条用例一个用户任务；固定 seed、时钟和网络，用条件等待代替 sleep；不留 skip、僵尸进程或生成物。

完成标准：每条新增测试都有用户认可的来源、过价值门且见过红。

## 清理与审计

**范围与授权**：范围按用户请求钉死——diff、路径、模块或全仓；全仓只在用户明确要求时做。"审计""看看""找找"是只读，交报告为止；"清理""删掉""精简"授权在范围内直接改。

**分工**：每条判决要读的东西多，范围跨 3 个以上模块时按 [fan-out](../fan-out/SKILL.md) 中档分两波；全仓清理或用户要求仔细时用高档。

- 按生产 owner 模块切片，每片带该模块的测试文件。多个模块共用的支撑代码（fixture、helper、全局 setup）不分给工人，由编排者最后处理，保持每个文件一个写者。
- 第一波只读：与编排者同级的实现角色逐条判决，格式同下文报告，另交**跨片线索**——疑似与别片重复证明的合同（测试名、合同一句话、位置），没有写"无"。
- 收口：同一合同跨片重复时保留最低可靠边界上的那条，其余改判 DELETE 并补证据；抽查每片的 KEEP 举证和 DELETE 证据，不全的退回。高档时 DELETE 另派不同模型的子代理独立复判，两份一致才删，不一致记为未定。审计到此合并报告。
- 第二波（清理）：原工人（或附定稿判决的新工人）只改片内文件并跑片内最窄验证；编排者最后删共享支撑代码和跨片的测试专用接缝，跑受影响验证。

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

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

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

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

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

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