znlgis 博客

GIS开发与技术分享

第五章:TDD、系统化调试与完成前验证

5.1 三个质量门禁

superpowers-zh 中最能改变 AI 编码质量的三个 Skills 是:

  • test-driven-development
  • systematic-debugging
  • verification-before-completion

它们分别对应三个问题:

问题 Skill 核心原则
如何新增或改变行为 TDD 没有失败测试,不写生产代码
如何处理 Bug 或失败 系统化调试 未找到根因,不提修复方案
如何宣称完成 完成前验证 没有新鲜验证证据,不许说完成

三者组合起来,形成从实现到修复再到交付的质量闭环。

5.2 TDD 的基本循环

test-driven-development 使用经典红-绿-重构循环:

  1. 红灯: 写一个最小失败测试,描述期望行为。
  2. 验证红灯: 运行测试,确认它因预期原因失败。
  3. 绿灯: 写最少的生产代码让测试通过。
  4. 验证绿灯: 运行测试,确认通过且输出干净。
  5. 重构: 在测试保持通过的前提下清理代码。
  6. 重复: 为下一个行为写下一个失败测试。

最关键的是「先失败」。如果测试一写就通过,说明它没有证明新行为缺失,可能只是覆盖了已有行为,或者测试写错了。

5.3 TDD 适用范围

Skill 要求以下场景始终使用 TDD:

  • 新功能。
  • Bug 修复。
  • 重构。
  • 行为变更。

可能例外的场景包括一次性原型、生成代码、纯配置文件等,但应询问用户或团队规则。不能因为「这很简单」就跳过。

5.4 好测试的标准

一个好测试应满足:

  • 只测一个行为。 如果测试名里出现多个「和」,通常要拆分。
  • 名称清晰。 例如「空邮箱应被拒绝」,不要叫「test1」。
  • 测试真实行为。 不要只验证 mock 被调用次数,而应验证用户可观察结果。
  • 展示期望 API。 测试应让读者知道代码应该如何使用。
  • 包含边界和错误场景。 尤其是 Bug 修复。

AI 常犯的错误是写过度 mock 的测试。mock 只在外部服务、时间、随机数、网络等不可控依赖上使用;核心业务逻辑应尽量用真实代码验证。

5.5 为什么后补测试不等于 TDD

后补测试回答的是「这段代码做了什么」,先写测试回答的是「这段代码应该做什么」。

后补测试会受到实现影响:

  • 容易测试实现细节而不是需求。
  • 容易遗漏实现者没有想到的边界。
  • 测试立即通过,不能证明它能捕获错误。
  • 不能驱动更好的 API 设计。

因此,如果 AI 已经先写了生产代码,再声称「现在补测试」,应要求它删除实现,从测试重新开始,除非用户明确允许例外。

5.6 系统化调试的四阶段

systematic-debugging 用于任何 Bug、测试失败、构建失败、性能问题或异常行为。它分四个阶段:

第一阶段:根因调查

  • 完整阅读错误信息和堆栈。
  • 稳定复现问题。
  • 检查近期变更。
  • 在多组件系统中收集边界证据。
  • 跟踪数据流,找到错误值从哪里产生。

在这个阶段之前,不允许提出修复方案。

第二阶段:模式分析

  • 找到代码库中正常工作的类似示例。
  • 对比参考实现。
  • 列出所有差异。
  • 理解依赖、配置和隐含假设。

这能避免 AI 在不了解现有模式时发明新写法。

第三阶段:假设与验证

  • 提出单一假设,例如「我认为失败是因为 X,因为 Y」。
  • 做最小改动验证假设。
  • 每次只改一个变量。
  • 验证失败就回到调查,而不是叠加更多补丁。

第四阶段:实施

  • 创建失败测试复现问题。
  • 修复根本原因,而不是症状。
  • 验证修复和回归。
  • 如果 3 次以上修复失败,应质疑架构,而不是继续猜。

5.7 调试中的危险信号

以下想法都说明 AI 正在偏离系统化调试:

  • 「先试着改一下 X。」
  • 「大概是这里的问题。」
  • 「一次改几个地方跑跑看。」
  • 「不完全理解,但这应该能行。」
  • 「先临时修一下,以后再查。」
  • 「错误太简单,不需要流程。」

遇到这些信号,应要求回到第一阶段:复现、读错误、查变更、收集证据。

5.8 完成前验证的门禁

verification-before-completion 的核心规则是:没有新鲜验证证据,不许宣称完成。

它要求在任何正面结论前完成 5 步:

  1. 确定: 什么命令能证明这个结论?
  2. 运行: 执行完整命令,不能依赖之前结果。
  3. 阅读: 看完整输出、退出码和失败数量。
  4. 验证: 判断输出是否支持结论。
  5. 陈述: 只有这时才能说测试通过、构建成功或任务完成。

如果构建命令无法运行,也应如实说明原因,而不是说「应该能通过」。

5.9 不同结论需要的证据

想表达的结论 需要的证据 不够的证据
测试通过 测试命令 exit 0,输出显示 0 failures 代码看起来对
构建成功 构建命令 exit 0 lint 通过
Bug 已修复 能复现原症状的测试通过 修改了相关代码
没有回归 相关回归测试或完整测试通过 手动点了一下
文档可构建 静态站点构建成功 Markdown 语法肉眼检查
PR 可提交 构建、测试、审查和安全验证完成 自我感觉完成

5.10 三个 Skill 的协同使用

新功能

  1. brainstorming 确认需求。
  2. writing-plans 拆计划。
  3. test-driven-development 每个行为先写失败测试。
  4. verification-before-completion 完成前跑测试和构建。

Bug 修复

  1. systematic-debugging 复现并定位根因。
  2. test-driven-development 写失败测试复现 Bug。
  3. 修复根因。
  4. verification-before-completion 跑回归和相关套件。

测试失败

  1. systematic-debugging 读错误和复现。
  2. 找到失败与近期变更的关系。
  3. 修复后运行失败测试。
  4. 再运行更大范围测试。

5.11 在没有测试框架的项目中怎么办

TDD 并不等于必须有完善测试框架。若项目没有测试框架,可以:

  • 使用现有脚本或命令行工具构造可重复验证。
  • 写最小临时验证脚本,但临时文件应放在 /tmp,不要提交。
  • 对文档项目运行静态站点构建。
  • 对配置变更运行对应工具的校验命令。
  • 对 UI 变更使用截图或浏览器检查作为补充证据。

如果确实无法自动化验证,应明确说明限制和手动验证步骤,不要伪装成测试通过。

5.12 对 AI 输出的监督提示

你可以这样要求 AI:

请严格使用 test-driven-development:先写一个失败测试,运行并展示失败原因,再写最少实现。
这是一个测试失败问题。不要直接修复,先使用 systematic-debugging 找根因。
在你说完成之前,请使用 verification-before-completion,运行能证明结果的命令并引用输出。

这些提示能把 AI 拉回流程,尤其在它开始说「应该」「看起来」「可能」时非常有用。

5.13 本章小结

TDD 防止未验证实现,系统化调试防止猜测式修复,完成前验证防止虚假完成。三者共同构成 superpowers-zh 的质量底座。只要坚持这三条,AI 编程的可靠性会显著提升;一旦跳过,AI 很容易回到「快速生成、慢速返工」的老路。