crucible 是做什么的?
何时使用
当要求编写、修复或审核 TypeScript 项目的单元测试时。
何时不使用
当编写端到端、集成、UI 或模拟测试时。
按项目选择退出
在激活之前,检查项目是否已选择退出此技能。查找以下任何一项:
- 项目根目录中存在
.no-crucible文件 AGENTS.md或.claude/settings.json中存在"crucible": false
如果发现任何退出信号,请不要自动激活;仅当用户在此对话中明确请求时才应用此技能。
规则
- 示例命令使用
pnpm,但始终使用项目的包管理器。 - 除非特别提示,否则不要修改应用程序代码。
- 当要求添加测试时,不要修改现有测试。
- 当要求修复测试时,不要添加新测试。
- 当要求审核测试时,先修复现有测试,然后根据需要添加测试以满足测试标准。
测试标准
单元测试应:
- 拥有根据 Vitest 的最大可能分支覆盖率。
- 杀死所有可杀死的变异体,实现提示范围内所有代码(而不仅仅是测试覆盖的代码)的最大可能变异分数。不可杀死的变异体会阻止达到 100 分。
- 尽可能使用基于属性的测试,除非只有少数输入情况存在。例如,一个只有一个布尔参数且没有隐式输入的函数只有两种可能的输入情况,因此不必费心将其设置为基于属性。
- (如果基于属性)arbitraries 应覆盖所有可能的参数和隐式输入。至关重要的是,不应排除任何可能在生产中发生的情况。如有必要或不明确,宁可将 arbitaries 设置得过于宽泛。
- 如果测试错误的代码或 arbitaries 被故意设置得过于宽泛,则应失败。将测试视为需求:它们应该相应命名,并且在用户将来修复 bug 后(用户修复函数和/或 arbitaries 后)应自动通过。不要使用
test.fails/.skip;这会让用户不那么明显地需要关注,因此测试在不该通过时通过是要避免的最高优先级错误。
检查分支覆盖率并运行测试:pnpm exec vitest run src/foo.test.ts --coverage
检查被测试代码中存活的变异体:pnpm exec stryker run --mutate "src/foo.ts"
验证测试设置
- 验证项目是否安装了以下所有包:
vitest fast-check @stryker-mutator/core @stryker-mutator/vitest-runner @stryker-mutator/typescript-checker - 验证 Vitest 和 Stryker 配置是否正确包含/排除文件。Stryker 不应检查仅包含测试数据的文件。
- 验证 Stryker 配置:
- 使用
clear-text报告器(其他报告器可选)。 - 使用 TypeScript 检查器并正确指向 TS 配置文件。
- 使用
- 查找任何其他可能导致测试编写工作流程问题的配置问题。
- 向用户报告发现结果。
编写测试
当要求编写新测试时,不要修改应用程序代码或现有测试。
首先,按照评估模块评估现有测试。使用模板创建一个新的汇报文件,并在其中记录初始覆盖率和变异分数。
其次,严格按照以下流程编写测试:
对模块接口中的每个函数/方法执行以下操作,完成一个函数后再开始下一个:
- 通过检查注释、函数名称、类型签名以及使用方式来确定函数的目的。如果不确定,在汇报中注明,跳过编写测试,并继续下一个函数。
- 记下函数实现中任何潜在的 bug。
- 根据其目的,识别函数对其参数和隐式输入所做的任何错误假设,并记下任何可能导致函数行为不正确的输入情况。例如,一个接受正整数但不验证其输入的函数对其输入做出了错误假设。
- 绝不修改预先存在的测试。如果在现有测试中发现任何问题,通知用户发现的问题。
- 通过执行以下操作编写新测试:
a. 使用
fast-check,创建基于属性的测试存根,其中 arbitaries 覆盖函数的所有可能参数和隐式输入。当被测试函数是异步时,使用fc.asyncProperty+await fc.assert(...)。如果有疑问,宁可将 arbitaries 设置得过于宽泛,并在汇报中提及,以便用户修复。将测试命名为期望行为的描述。 b. 使用vitest,在存根中编写断言,涵盖任何返回值以及副作用,例如抛出异常、变异输入、变异/重新分配外部作用域的变量等。确保测试断言期望的行为,从而捕获先前识别的错误假设/bug 并相应失败。 - 重新检查覆盖率:
pnpm exec vitest run src/foo.test.ts --coverage - 仅对当前函数进行变异测试:
pnpm exec stryker run --mutate "src/foo.ts:<起始行号>-<结束行号>" - 修改新编写的测试,并重复步骤 5a 和 5b,直到该函数满足测试标准(上面列出)。
- 在汇报中为该函数添加一个部分。
为所有函数编写测试后,再次检查是否为每个函数添加了汇报部分,并完成总结部分的编写。
审核测试
如果提示审核整个项目,检查项目范围的代码覆盖率和变异分数,并推荐需要进行更详细审核或添加/更新测试的区域。偏向于推荐代码库中最重要且最容易出现 bug 的区域——核心业务逻辑、事件驱动代码和复杂函数。
如果提示审核项目的一部分,查找(在提示范围内的)没有测试的模块或函数,并在审核后汇报中向用户报告。
对于提示范围内有测试的每个模块,请遵循评估模块。然后,如果在评估修复后仍有任何测试标准未满足,请遵循编写测试以填补剩余空白。
评估模块
使用此流程评估现有测试文件是否符合测试标准——无论是在编写新测试之前还是在审核时。
- 使用 Vitest 检查代码覆盖率:
pnpm exec vitest run src/foo.test.ts --coverage。 - 使用 Stryker Mutator 检查存活的变异体:
pnpm exec stryker run --mutate "src/foo.ts"。 - 对于模块中具有测试的每个函数:
- 对于基于属性的测试:
- 查找函数可以接受而不抛出异常或返回错误值,但 arbitaries 无法生成的输入。根据需要扩大 arbitaries 以包含此类输入。
- 查找 arbitaries 未覆盖的输入,特别是隐式输入,例如类字段、全局变量、日期/时间和随机生成的数字。为此添加 arbitaries。
- 修复模拟实现中的所有错误。
- 查找性能瓶颈。
- 对于基于属性的测试:
- 在汇报中记录覆盖率和变异分数,以及任何发现结果。
测试汇报
根据以下模板格式化汇报。如果在函数小节中没有什么可报告的,只需写上“无”。将汇报输出到 debriefs/ 目录中的 Markdown 文件。
汇报:{moduleName/fileName} {yyyy-mm-dd}
总结
覆盖率(分支) 之前:X% → 之后:Y% 无法覆盖的行:{行范围}
变异分数(总计) 之前:X% → 之后:Y%
问题
{错误假设、发现的 bug、测试中发现的问题以及失败测试的要点列表。每个列表项应以发现问题的文件相对于项目根目录的路径和行号开头}
{函数名称}
函数目的 {一句话描述该函数应该做什么} (置信度:高 | 不确定) {如果不确定:什么不清楚以及原因}
发现的错误假设
- (第 {行号/范围} 行):{函数未验证的关于输入类型/范围/形状的假设}
实现中发现的 bug
- (第 {行号/范围} 行):{描述 + 行号}
预先存在的测试中发现的问题
- (第 {行号/范围} 行):{问题描述 + 测试名称}
编写的测试用例
- {每个测试的属性/不变量的简要描述}
失败的测试
- (第 {行号/范围} 行):{测试名称 + 原因}
不可杀死的变异体
- (第 {行号/范围} 行):{简要描述}