SuperAPI AI 工具集

characterisation-tests

测试与质量

查看项目

更新于 10-16 11:29

技能介绍

用途

Characterisation Tests(特征测试)用于在修改已有代码前,先记录并固定代码当前的真实行为,尤其适用于以下情况:

  • 要修改的既有代码没有测试或测试不足
  • 规格说明缺失、不完整,或与实际运行系统相矛盾
  • 代码过于复杂,仅靠阅读难以推断行为
  • 面临遗留代码困境:需要测试才能安全重构,但代码抗拒测试
  • 需要在修改函数前先理解它实际返回什么

该技能专门用于理解和锁定代码“当前做什么”,而不是“应该做什么”。

核心概念

特征测试是描述一段代码实际行为的测试。其中没有“它应该做这个”的断言,测试记录的是系统真实行为。特征测试不具备道德权威,它们不断言正确性,只检测变更。当特征测试失败时,由人来判断该变更是否有意为之。它也被称为 golden master testing 或 approval testing。

主要能力

  • 通过失败断言发现真实行为:先写一个已知会失败的断言(如使用 "PLACEHOLDER" 占位值),运行测试,让测试运行器显示实际值,再将测试改为期望代码实际产生的行为。
  • 强调绝不能输入未经运行产生的期望值。每个期望值都必须来自执行真实模块:失败的占位断言、记录的快照,或导入该模块的临时测试。通过阅读代码推算出答案,或把函数复制到临时脚本中运行副本,都不属于特征化。
  • 对多行输出(渲染文本、格式化文档、大型对象)使用内联快照(inline snapshot)固定行为,而不是手写字符串。调用无参数的 toMatchInlineSnapshot(),运行一次,运行器会把观察到的输出写入测试文件。
  • 命名与识别规范:测试名中使用 characterises 与行为驱动测试区分;文件后缀使用 .characterisation.test.ts;在文件顶部添加块注释说明目的和计划生命周期。
  • 对可疑行为显式标记,不静默“修复”。
  • 支持异步代码特征化,包括错误路径、时间依赖行为、流与事件。

使用方式

逐步算法:

  1. 在测试夹具中使用一段代码
  2. 写一个已知会失败的断言(使用占位值)
  3. 运行测试,让失败告诉你行为,测试运行器显示实际值
  4. 修改测试,让它期望代码实际产生的行为
  5. 重复,让好奇心引导,代码本身会提示接下来测试什么

文档还提到按需加载 resources/ 目录中的深度资源:writing-process.md(完整特征化流程的示例,包含针对性测试、异步代码和何时停止的指导)、modern-tooling.md(被测代码返回多行文本、渲染文档或大型对象时,或想要遍历大量输入组合、行为不确定时使用)。

适用场景

  • 修改无测试或测试不足的既有代码
  • 规格缺失、不完整或与运行系统矛盾
  • 代码过于复杂,难以仅靠阅读推断
  • 修改函数前需要理解其实际返回

限制与不适用场景

  • 绿地代码:新代码应从开始就测试驱动(见 tdd)
  • 已有规格:如果需求清晰且代码较新,应编写断言预期行为的行为驱动测试,而不是记录代码任意行为的特征测试
  • 代码已有充分测试:只特征化未测试部分,不要重复已有覆盖
  • 不应作为永久测试策略:特征测试是脚手架,重构时应替换为正式测试

其他限制:不需要对 100% 代码库做特征化,只需覆盖即将修改的代码加一层外扩。特征测试是临时的,流程为:特征化(固定当前行为作为安全网)、重构(在特征测试检测行为变化的同时重构代码)、替换(随着理解加深,编写断言预期行为的正式测试)、移除(当正式测试覆盖相同行为后退役特征测试)。发现 bug 时:如果系统已部署,可能有人依赖“有 bug”的行为,应记录、标记为可疑、保留直至做出兼容性决定,并在修改前上报;如果系统尚未部署,兼容性风险较低,但未部署并不等于正确,应先从权威需求或用户决策确认预期行为,再记录观察到的失败、修复,并用预期行为回归测试替换旧行为特征测试。

文档中的常见错误对照表包括:把特征测试当作永久测试、在特征测试中“修复”bug、试图特征化整个代码库、基于代码“应该做什么”编写特征测试、通过阅读代码或临时脚本副本计算期望值、把覆盖率当作测试强度的证明、对新代码使用特征测试、用 vi.mock() 感知而非参数注入、不 await 异步结果等。