better-test · diff
git:20261008.c9566bf to git:20261009.443953a
25 added, 29 removed. Audit A to A.
---
name: better-test
- description: 写、改、审查或清理测试时使用:价值门、反模式、见红、接缝与 mock 边界、DELETE/REWRITE/KEEP 判决,以及自动化够不到时的取证。
+ description: 验证改动、写测试、审查或清理测试时使用:验证方法、什么测试值得留、端到端测试写法、DELETE/REWRITE/KEEP 判决。
---
# Better Test
- 测试是负债:每条持久测试都必须在失败时说明某个真实行为坏了。覆盖率、测试数量、"每个函数一条"都不是目标。
+ 测试是负债。agent 自己写的单元、集成测试只复述实现:测试和代码出自同一份理解,不比实现更准;实测不提高正确率,只增加时间和费用。持久测试只有两种来源:用户给出或认可的用例,和覆盖关键用户流程的端到端测试。
只走一个分支:
- 1. 写或改测试 → **写测试**。
- 2. 审计或清理既有测试 → **清理与审计**。
- 3. 修已知 bug → 先在 bug 真实发生的接缝上写出复现用户准确症状的失败用例,再修;这条回归用例按**写测试**过价值门。
- 4. 行为值得保护但自动化碰不到 → **取证**。
+ 1. 验证一次改动 → **验证**。
+ 2. 用户要求测试,或工单给了用例 → **写测试**。
+ 3. 审计或清理既有测试 → **清理与审计**。
+ ## 验证
+
+ 按接近用户真实路径的顺序选最短方法,证据来自本次执行:
+
+ 1. 项目已有测试、类型检查、构建中受影响的部分,或直接运行改动的命令;
+ 2. **better-browser-use**:页面、DOM、截图、console、network;
+ 3. curl 或 CLI:直接调用并核对响应内容;
+ 4. **better-computer-use**:桌面应用或真机;
+ 5. 都不可行:给用户"打开哪里 → 做什么 → 应看到什么"的最短清单,标注未验证。
+
+ 修 bug 先按用户描述实际复现症状,修完走同一路径确认消失。同一路径第二次需要人工取证时,向用户提议补一条端到端测试。
+
## 价值门
- 写新测试与判既有测试共用这一道门,六条必须全部成立:
+ 判既有测试和写新测试共用这一道门,六条全部成立才留:
- 1. **有来源**:保护的行为有独立依据——需求、bug、公开合同、可访问性规则或规格示例。
+ 1. **有来源**:保护的行为来自用户的需求、用例、bug 报告、公开合同或规格示例,不是 agent 对实现的理解。
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. **验证**:跑最窄受影响验证。
+ 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**:不过门,且写全三项证据——它实际能抓住什么失败;该行为别处更强的证明,或为什么不需要证明;它或它依赖的测试专用接缝当初为何存在。
+ - **DELETE**:不过门(agent 写的、只复述实现的单元与集成测试通常落在这里),且写全三项证据——它实际能抓住什么失败;该行为别处更强的证明,或为什么不需要证明;它或它依赖的测试专用接缝当初为何存在。
- **未定**: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. 都不可行:给用户"打开哪里 → 做什么 → 应看到什么"的最短清单,标注未验证。
-
- 证据必须来自本次执行。只有过价值门、值得长期保护的缺口才记测试债;同一路径第二次需要人工取证时,向用户提出补一个自动化验证入口。