SuperAPI AI 工具集

test-driven-development

测试与质量

查看项目

更新于 10-16 06:10

技能介绍

用途

该技能用于指导测试驱动开发(TDD)实践,适用于实现任何功能或修复缺陷之前。核心原则是:先写测试,看它失败,再写最小代码使其通过。如果没亲眼看到测试失败,就无法确定测试测的是正确的东西。违反规则的字面要求,就是违反规则的精神。

何时使用

始终适用于:新功能、缺陷修复、重构、行为变更。

例外情况(需询问人类伙伴):一次性原型、生成的代码、配置文件。

如果出现“就这一次跳过 TDD”的想法,应停止,这属于合理化借口。

铁律

没有失败的测试,就不写生产代码。

如果在测试之前写了代码,就删除它,重新开始。没有例外:不要把它留作“参考”,不要在写测试时“改编”它,不要看它。删除就是删除。从测试重新实现。

红-绿-重构循环

RED:写一个失败测试,展示应该发生什么。要求:一个行为、清晰名称、真实代码(除非不可避免,否则不用 mock)。

验证 RED:强制步骤,绝不跳过。运行测试,确认测试失败(不是报错)、失败信息符合预期、失败原因是功能缺失(不是拼写错误)。如果测试通过,说明测的是已有行为,需修正测试;如果测试报错,先修复错误,重新运行直到正确失败。

GREEN:写最简单的代码让测试通过。不要添加功能、重构其他代码或超出测试范围“改进”。

验证 GREEN:强制步骤。运行测试,确认测试通过、其他测试仍通过、输出干净(无错误、警告)。如果测试失败,修代码而不是改测试;如果其他测试失败,立即修复。

REFACTOR:仅在绿灯后进行。消除重复、改进命名、提取辅助函数。保持测试绿灯,不添加行为。

重复:为下一个功能写下一个失败测试。

好测试的标准

最小:只测一件事。名称里有“and”?拆分它。

清晰:名称描述行为,而不是 test1。

展示意图:演示期望的 API,而不是掩盖代码应该做什么。

为什么顺序重要

“我之后写测试来验证它能工作”:代码后写的测试会立即通过。立即通过证明不了什么:可能测错东西、可能测实现而非行为、可能漏掉忘记的边界情况、你从未看到它捕获缺陷。测试先行强制你看到测试失败,证明它确实在测东西。

“我已经手动测试了所有边界情况”:手动测试是临时的。没有测试记录、代码变化时无法重跑、压力下容易忘记情况。“我试的时候能用”不等于全面。自动化测试是系统性的,每次运行方式相同。

“删除 X 小时的工作很浪费”:沉没成本谬误。时间已经过去。现在的选择是:删除并用 TDD 重写(再花 X 小时,高置信度),或保留并之后加测试(30 分钟,低置信度,可能有缺陷)。真正的“浪费”是保留你无法信任的代码。没有真实测试的工作代码是技术债。

“TDD 是教条,务实意味着变通”:TDD 就是务实的:在提交前发现缺陷(比之后调试更快)、防止回归(测试立即捕获破坏)、记录行为(测试展示如何使用代码)、支持重构(自由修改,测试捕获破坏)。“务实”的捷径等于在生产环境调试,更慢。

“之后写测试达到同样目标——重要的是精神不是仪式”:不对。之后写测试回答“这做了什么?”;测试先行回答“这应该做什么?”之后写测试会被实现偏见影响,你测的是你构建的东西,不是要求的东西;你验证的是记得的边界情况,不是发现的边界情况。测试先行强制在实现前发现边界情况。之后 30 分钟的测试不等于 TDD,你得到覆盖率,但失去测试有效的证明。

常见合理化借口

“太简单不用测”:简单代码也会坏,测试只需 30 秒。

“我之后测”:立即通过的测试证明不了什么。

“之后测达到同样目标”:之后测 = “这做什么?”;先测 = “这应该做什么?”

“已经手动测过”:临时不等于系统,没有记录,无法重跑。

“删除 X 小时很浪费”:沉没成本谬误,保留未验证代码是技术债。

“留作参考,先写测试”:你会改编它,那就是之后测。删除就是删除。

“需要先探索”:可以,扔掉探索,从 TDD 开始。

“测试难 = 设计不清”:听测试的。难测 = 难用。

“TDD 会拖慢我”:TDD 比调试快。务实 = 测试先行。

“手动测试更快”:手动不证明边界情况,每次变更都要重测。

“现有代码没有测试”:你在改进它,为现有代码加测试。

红旗信号——停止并重新开始

测试前有代码;实现后写测试;测试立即通过;无法解释测试为何失败;测试“稍后”添加;合理化“就这一次”;“我已经手动测过”;“之后测达到同样目的”;“重要的是精神不是仪式”;“留作参考”或“改编现有代码”;“已经花了 X 小时,删除浪费”;“TDD 是教条,我很务实”;“这不一样因为……”——所有这些都意味着:删除代码,从 TDD 重新开始。

示例:缺陷修复

缺陷:空邮箱被接受。

RED:写测试 rejects empty email,断言提交空邮箱时返回错误 'Email required'。

验证 RED:运行 npm test,看到 FAIL: expected 'Email required', got undefined。

GREEN:在 submitForm 中检查 data.email?.trim(),为空则返回 { error: 'Email required' }。

验证 GREEN:运行 npm test,看到 PASS。

REFACTOR:如需要,为多个字段提取验证逻辑。

验证检查清单

标记工作完成前:每个新函数/方法都有测试;在实现前看到每个测试失败;每个测试因预期原因失败(功能缺失,不是拼写错误);写最小代码让每个测试通过;所有测试通过;输出干净(无错误、警告);测试使用真实代码(仅在不可避免时用 mock);覆盖边界情况和错误。无法勾选所有项?你跳过了 TDD,重新开始。

遇到困难时

不知道如何测试:写期望的 API,先写断言,询问人类伙伴。

测试太复杂:设计太复杂,简化接口。

必须 mock 一切:代码太耦合,使用依赖注入。

测试设置巨大:提取辅助函数;仍然复杂?简化设计。

调试集成

发现缺陷?写一个复现它的失败测试。遵循 TDD 循环。测试证明修复并防止回归。没有测试绝不修复缺陷。

最终规则

生产代码 → 测试存在且先失败。否则 → 不是 TDD。未经人类伙伴许可,没有例外。