关于 legacy-testing
legacy-testing 是为缺少测试的代码建立保护网、再进行重构的 Skill。它先评估关键行为和入口,拟定分步计划供批准,之后指导执行并汇总结果。
主要功能
- 01评估现有测试与未覆盖区域
- 02识别关键行为和外部入口
- 03制定待批准的测试计划
- 04指导测试与度量步骤
- 05将重构安排在度量之后
适用场景
- 01重构旧代码前建立测试保护
- 02为金额或权限规则选择测试
- 03查找外部输入可触发的崩溃
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ dmoka/ skills --skill legacy-testing安装后在 Claude Code 中输入/legacy-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
examples/ticketbay-fees.mdreferences/approval-tests.mdcharacterize.mdmake-it-testable.mdmeasure.mdproperty-testing.mdrefactor.mdscripts/assess.sh详细介绍
遗留代码测试
遗留代码是指没有测试的代码。测试是承载业务行为的安全网,应在任何人重构之前搭建。先评估,再提出计划,等待批准,然后执行。
步骤
- 评估。 在仓库根目录运行
scripts/assess.sh [path];路径可以是文件或文件夹。该脚本会列出仓库中所有导入目标的测试,以及没有被任何测试导入的文件。阅读这些测试和最大的三个未测试区域,并针对每个区域指出其关键行为(资金、权限、不得丢失的数据)。然后列出目标的入口点:所有来自外部输入的路径(HTTP 路由、CLI 命令、消息处理器、解析器、接收用户数据的导出函数);在测试之外搜索目标的导入,并沿着调用链追踪到边界。完成标准:能够针对每个区域说明它的用途、依赖项、现有测试覆盖情况、入口点,以及出错会造成什么影响。 - 提出计划。 使用下方模板编写计划:列出此仓库需要哪些步骤、执行顺序,以及每一步的原因。从菜单中选择;评估认为不需要的步骤就跳过。默认将重构作为计划的最后一步,并说明重构目标和原因;只有用户明确提出时才省略。完成标准:每个选定步骤都注明目标(文件或函数)及原因。
- 等待一次批准。 展示计划并等待。用户会评估两点:计划是否针对关键行为,以及测试检查的是行为而非当前实现。批准计划也就批准了其中的重构步骤。完成标准:用户批准或修改计划。
- 不间断地执行已批准的计划。 按顺序执行各步骤:打开该步骤的操作指南,完成操作,运行测试套件,然后开始下一步。步骤之间不要询问“要继续吗?”;批准计划就表示批准了其中的所有步骤。只有在以下情况才暂停并询问:标记某个变异体为等价变异体、修改计划中未列出的 CI 或配置,或确认并修复疑似 Bug,而不是将其固定为现有行为。只有在衡量步骤达到目标后才开始重构,并将重构与测试分开提交;提交不得改变任何行为。完成标准:每个步骤各自的完成检查均通过,且测试套件全部通过。
- 最后统一汇报: 汇报前后得分、新增的测试、疑似 Bug(包括崩溃搜寻属性测试发现的崩溃)、重构所做的更改,以及尚未解决的决策。先删除所有 TEMPORARY 文件。
菜单
| 步骤 | 适用情况 | 操作指南 |
|---|---|---|
| 让代码可测试 | 代码直接访问数据库、API、队列、时钟或文件系统 | references/make-it-testable.md |
| 特征化 | 无人能说清代码目前的行为 | references/characterize.md |
| 审批测试 | 输出内容很大:JSON、HTML、报告 | references/approval-tests.md |
| 属性测试:崩溃搜寻 | 来自外部输入的每个目标(端点、CLI 命令、处理器、解析器):生成的有效和无效输入都不得导致入口点崩溃 | references/property-testing.md |
| 属性测试:不变量 | 带有边界情况的规则:资金、日期、限制、往返转换 | references/property-testing.md |
| 衡量 | 宣布安全网完成之前:覆盖率显示哪些代码从未运行,变异测试显示哪些代码从未被检查 | references/measure.md |
| 重构 | 每个计划都应将重构作为最后一步,在衡量达到目标后执行;只有用户明确提出时才省略 | references/refactor.md |
计划模板
Area: <files> — load-bearing: <behaviour>
Entry points: <route / command / handler / exported function> → <target>
1. <step> on <target> — why: <reason from the assessment>
2. ...
n. Crash-hunt property on <each entry point> — why: outside input reaches it; no input may crash it
n+1. Invariant properties on <rules> — why: <the rules with edge cases>
last. Refactor <target>: <the structure change> — why: <what the assessment found hard to read or change>
Skipped: <step> — why not needed here
Target: mutation score on <area> ≥ <n>% (coverage is a hint, not the goal)
注意事项
- 特征测试会固定当前行为,包括 bug。当固定的结果看起来不对时,也要照样固定下来,将测试命名为
pins current behaviour — suspected bug: <what is wrong>,并在最终报告中列出;修复它属于单独的后续变更。 - 如果某个依赖运行很快,或承载业务逻辑,就在测试中使用真实依赖(内存数据库或容器化数据库、真实规则引擎)。只有对运行缓慢或不可靠的边界,才使用测试替身。
- 审批测试是脚手架。请在文件名或注释中将它们标记为临时测试,以便功能测试覆盖相关行为后将其删除。
- 崩溃搜寻属性测试发现的崩溃属于疑似 bug,即使没有用户遇到过也一样:固定缩减后的输入,将其列出,并继续运行属性测试搜寻。阅读代码时发现的崩溃不能取代属性测试:固定这个已发现的崩溃,并且仍要为该入口添加属性测试。
- 不稳定的测试不能充当防护网。如果测试在代码未变更时有时通过、有时失败,就将其隔离,并在以此为基础继续工作前报告。
- 对于耗时较长的测试套件:工作时运行覆盖相关区域的最窄命令(一个文件、一个文件夹),并在每个步骤结束时运行完整测试套件。
- 将变异分数的目标设定在关键区域,而不是整个代码库;全库统计会让核心业务代码的分数被平均值掩盖。
- 每次变异测试运行都要限定范围,并运行两次:只对目标文件进行变异(Stryker:
--mutate <file>),并且只运行导入该文件的测试(assess.sh <file>会列出这些测试)。如果项目提供了脚本,就使用它;否则请参阅references/measure.md中的“限定范围运行”。代码库自身的配置可能会在空跑时运行整个测试套件。
最后验证时间:2026-10-04,使用 Claude Code 2.1.289,仅规划(未执行),TicketBay 的 legacy/reporting 分支:针对 src/finance/payouts.ts,2 个计划中有 2 个在 GET /api/v1/organizer/payouts 上列出了崩溃搜寻属性测试,并将重构作为最后一步,且未被要求(之前的版本为 0/2,两个计划都标注了“Skipped: Refactor”);针对 src/finance/invoices.ts,1 个计划中的 1 个也做到了这一点。此前,2026-10-04,在 TicketBay 的 src/db/admin-queries.ts 上:变异分数从 62.7% 提升到 98.5%,固定了 4 个疑似 bug。这次试验促成了当前版本:assess.sh 不接受文件路径,且会漏掉目标文件夹之外的测试;Agent 在每个步骤后都会暂停;未限定范围的空跑耗时超过 10 分钟仍未完成,而限定范围的空跑耗时 1.5 分钟。更早之前:2026-09-29,使用 2.1.284(包含已删除测试的 TicketBay,以及一个未经测试的真实 MERN 应用)。
评论
支持 Markdown