关于 test-audit
面向编写或审查测试的开发者,强调测试应能独立发现与用户相关的缺陷,并提供测试设计、回归覆盖、模拟和审计既有测试的准则。
主要功能
- 01编写测试前的四项检查
- 02回归测试验证
- 03模拟使用指导
- 04基于变异的覆盖检查
- 05基于证据的测试审计
适用场景
- 01评估新测试是否能保护行为
- 02验证回归测试能否复现缺陷
- 03依据记录的证据精简测试
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ t0msilver/ skills --skill test-audit安装后在 Claude Code 中输入/test-audit确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
reference/campaign.mdjunk-patterns.mdsources.md详细介绍
测试审查
测试通过捕获人们会称为 bug 的问题,证明其维护成本物有所值。编写测试和决定删除哪些现有测试,都适用同一标准。若要在一次改动中精简整个子系统的测试,请先阅读 reference/campaign.md。
编写测试之前
回答以下四个问题;只要有一个问题答不上来,就先不要添加测试。
- 什么生产代码改动会让它失败?这种改动是 bug,还是一种决策? 如果只有有意做出的决策才会让它失败(常量的值、确切措辞、私有结构),那它就是变更探测器:重设计时会触发,出现 bug 时却不会。应改为测试依赖该决策的行为:不要测
MAX_RETRIES == 5,而要测“不会发生第六次尝试”。 - 预期值从何而来? 应来自字面值、计算示例或规范。绝不能来自被测代码或其辅助函数:
expect(f(x)).toBe(f(x))必然会通过。使用实现自身的公式重新计算预期值,也会把该公式中的 bug 一并带入。 - 为什么现有覆盖无法捕获它? 每项契约都应由一个测试在最合适的边界进行验证,通常是公共接口。增加第二层测试,必须有其所属测试无法触及的风险,例如传输或生命周期故障。与其新增近似重复的测试,不如在现有表格测试中增加一行。
- 它是否需要生产代码调用方并不需要的生产代码接缝? 例如为了测试而添加的导出、标志、getter、重置钩子或
destroy()。如果是,应改为在真实边界进行测试;仅测试清理所需的内容应放入测试工具中。
然后根据 垃圾测试模式 检查。若命中其中一种模式,就不符合准入标准;除非保留标准明确指出它独立保护的契约。如果测试会因保持行为不变的重构而失败,那它断言的就是实现细节;合并前,应在所属边界重写测试。
不要测试框架(例如断言路由器会调用你注册的处理程序),也不要测试琐碎代码:只有当构造函数、getter 和单纯的转发逻辑会进行验证、规范化、设定默认值、派生数据或产生副作用时,才值得为它们编写测试。
回归测试
回归测试必须在修复前的代码上失败,失败原因应与 bug 描述一致,并在修复后通过。应在旧代码上运行测试来验证。若测试因 bug 不会导致的问题而失败(导入失败、缺少选择器、设置损坏),就无法证明任何东西;应修复测试,直到它以与 bug 相同的方式失败。始终没有失败过的回归测试,证明的只是 mock,而不是修复有效。在所属边界编写一个测试;不要在它经过的每一层重复同一场景。
Mock
- 绝不要断言 mock。若断言只会在 mock 存在时通过、移除 mock 时失败,那它对你的代码毫无证明作用。
- Mock 方法之前,先列出它的副作用。应 mock 测试所依赖层级之下的慢速或外部层,而不是 mock 方法本身。
- Mock 响应应完整复现真实结构,而不只是包含该测试会读取的字段;不完整的 mock 可能通过测试,而集成却会失败。
- 当参数、调用次数或顺序属于契约时,应对它们进行断言。成功、错误和格式错误等每个分支都应有各自的 fixture,避免错误的分支也能满足测试。
- 如果 mock 设置比测试本身还复杂,或者你说不清为什么需要 mock,就改用真实组件,并配合虚拟网络或文件系统。
完成之前
在脑中修改生产代码:错误的常量或参数、错误的分支、缺失的副作用、空返回值,以及缺少对空值、零、null、未授权或格式错误输入的检查。每一种情况都至少应有一个测试失败。若某种修改没有被任何测试捕获,就说明有行为未受保护,或测试是恒真式。
审查现有测试
- 阅读根目录和作用域内的
AGENTS.md/CLAUDE.md,然后针对每个候选测试,阅读完整测试、它覆盖的生产代码、调用方、重叠测试、CI 路由以及用于了解其存在原因的git log。如果测试声称验证了依赖项支持的行为,还要阅读该依赖项的源代码或类型定义。 - 发现阶段保持只读。优先选择少数证据充分的候选项,而不是列出一长串推测性候选项。范围较广时,按领域拆分任务,并行安排只读 Agent。
- 编辑前,记录每个候选项的证据。缺少任何字段,都说明该候选项尚未准备好删除。
- 每次变更只编辑一个连贯的批次。删除测试专用的导出、标志、包装器,以及因删除而不再需要的无用生产代码路径,而不是保留别名。将保留的回归测试移到所属测试套件。不要添加重复描述同一实现的替代测试。
- 运行最小范围的所属测试和同级测试,然后运行仓库对已变更文件要求的检查。删除源码文本断言时,运行负责实际契约的脚本或试运行。
- 分别报告生产代码和测试代码的行数变化(
git diff --numstat),并列出保留的、看似无用的测试及其保留理由。
注意事项
- 不能仅因测试是静态的或运行缓慢就删除。 读取源码文本的测试,可能是针对面向用户的键、字节或路径最廉价的独立保护措施。如果契约发生变化时它会失败,并且重命名标识符后仍能发挥作用,就应保留。其他测试应符合保留标准。
- 如果某个删除候选测试在基线分支上失败,可能是产品缺陷。 重现该问题并修复生产代码;不要删除发现问题的测试。
- 根据断言判断测试,而不是根据名称。 一个名为“退役窗口”的测试,曾经断言窗口没有被清除。
- 检查负向测试是否确实因正确原因通过。 如果拒绝是由其他防护条件触发,或者生产路径根本不会走到被拒绝的环节,那么测试虽然通过,却没有保护任何内容。
- 不要为了减少数量而删除测试。 对不确定的候选项,暂且保留。优先确保剩余内容可信。
- 测试套件运行期间,不要在同一个检出目录中更改测试。 监视模式和并行运行器可能会读取只编辑了一半的文件,导致报告并不存在的失败。