关于 moai-workflow-tdd
moai-workflow-tdd 是面向新功能开发的测试驱动开发技能,将工作组织为 RED-GREEN-REFACTOR 循环,先用失败测试定义行为,再编写实现。
主要功能
- 01RED-GREEN-REFACTOR 开发循环
- 02实现前编写行为测试
- 03以通过测试为目标的最小实现
- 04持续运行测试验证重构
- 05覆盖率与质量验证
适用场景
- 01依据契约开发新的 API 端点
- 02构建行为定义明确的 UI 组件
- 03通过小步测试优先循环添加功能
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ modu-ai/ moai-adk --skill moai-workflow-tdd安装后在 Claude Code 中输入/moai-workflow-tdd确认已加载。
附带文件references/examples.mdreferences/reference.md
详细介绍
测试驱动开发(TDD)工作流
开发模式配置(重要)
[NOTE] 此工作流根据 .moai/config/sections/quality.yaml 选择:
constitution:
development_mode: tdd # or ddd
何时使用此工作流:
development_mode: tdd→ 使用 TDD(此工作流,默认)development_mode: ddd→ 改用 DDD(moai-workflow-ddd)
主要区别:
- TDD(默认):所有工作均采用测试优先开发,包括先进行 RED 分析的棕地项目
- DDD:适用于测试覆盖率较低的现有代码库,优先编写特征测试
快速参考
测试驱动开发通过在实现之前用测试定义预期行为,为创建新功能提供一种严谨的方法。
核心循环 - RED-GREEN-REFACTOR:
- RED:编写一个定义期望行为的失败测试
- GREEN:编写最少量的代码,使测试通过
- REFACTOR:在保持测试通过的同时改进代码结构
何时使用 TDD:
- 从零开始创建新功能
- 构建没有现有依赖项的独立模块
- 由行为规范驱动开发
- 具有明确契约的新 API 端点
- 具有明确定义行为的新 UI 组件
- 绿地项目(较少见——通常 Hybrid 更合适)
何时不使用 TDD:
- 重构现有代码(改用 DDD)
- 以保持行为为首要目标时
- 没有测试覆盖率的遗留代码库(先使用 DDD)
- 修改现有文件时(可考虑 Hybrid 模式)
核心理念
TDD 与 DDD 对比
TDD 方法:
- 循环:RED-GREEN-REFACTOR
- 目标:通过测试创建新功能
- 起点:尚无代码
- 测试类型:定义预期行为的规范测试
- 结果:具有测试覆盖率的新可用代码
DDD 方法:
- 循环:ANALYZE-PRESERVE-IMPROVE
- 目标:在不改变行为的前提下改进结构
- 起点:行为已明确的现有代码
- 测试类型:记录当前行为的特征测试
- 结果:结构更完善且行为完全相同的代码
测试优先原则
TDD 的黄金法则是,必须先于实现代码编写测试:
- 测试定义契约
- 测试记录预期行为
- 测试能立即发现回归问题
- 实现由测试要求驱动
实现指南
阶段 1:RED - 编写失败测试
RED 阶段侧重于通过失败测试定义期望行为。
编写有效测试
在编写任何实现代码之前:
- 清楚理解需求
- 以测试形式定义预期行为
- 一次编写一个测试
- 让测试保持专注且具体
- 使用能说明行为的描述性测试名称
测试结构
遵循 Arrange-Act-Assert 模式:
- Arrange:准备测试数据和依赖项
- Act:执行被测代码
- Assert:验证预期结果
验证
测试最初必须失败:
- 确认测试确实测试了某项内容
- 确保测试不是意外通过
- 记录当前状态与期望状态之间的差距
阶段 2:GREEN - 让测试通过
GREEN 阶段侧重于编写最少量的代码以满足测试要求。
最小化实现
只编写足以让测试通过的代码:
- 不要过度设计
- 不要添加测试未要求的功能
- 注重正确性,而非完美
- 必要时可将值硬编码(之后再重构)
验证
运行测试,确认其通过:
- 所有断言都必须成功
- 其他测试不得失败
- 实现满足测试要求
阶段 3:REFACTOR - 改进代码
REFACTOR 阶段侧重于在保持行为不变的同时提升代码质量。
安全重构
以通过的测试作为安全网:
- 移除重复代码
- 改进命名和可读性
- 提取方法和类
- 在适当时应用设计模式
持续验证
每次重构后:
- 运行所有测试
- 如果任何测试失败,立即回滚
- 测试通过后提交
TDD 工作流执行
标准 TDD 会话
通过 manager-develop 执行 TDD 时:
步骤 1 - 理解需求:
- 阅读 SPEC 文档,了解功能范围
- 根据验收标准确定测试用例
- 规划测试实现顺序
步骤 2 - RED 阶段:
- 编写第一个失败的测试
- 确认测试因正确的原因而失败
- 记录预期行为
步骤 3 - GREEN 阶段:
- 编写最精简的实现
- 运行测试,确认其通过
- 开始下一个测试
步骤 4 - REFACTOR 阶段:
- 检查代码,寻找改进空间
- 在测试的保护下进行重构
- 提交整洁的代码
步骤 5 - 重复:
- 持续执行 RED-GREEN-REFACTOR 循环
- 直到实现所有需求
- 直到通过所有验收标准
TDD 循环模式
对于需要多个测试用例的功能:
- 预先确定所有测试用例
- 根据依赖关系和复杂度确定优先级
- 为每个用例执行 RED-GREEN-REFACTOR
- 持续维护测试套件
质量指标
TDD 成功标准
测试覆盖率(必需):
- 每次提交的覆盖率至少为 80%
- 新代码建议达到 90%
- 测试覆盖所有公共接口
代码质量(目标):
- 所有测试均通过
- 不在实现完成后编写测试
- 测试名称清晰,能说明行为
- 以最精简的实现满足测试要求
TDD 专项 TRUST 验证
结合 TDD 重点应用 TRUST 5 框架:
- 已测试:测试优先的方法确保覆盖率
- 可读性:测试记录预期行为
- 统一性:风格和结构与周边代码保持一致
- 安全性:在实现之前编写安全测试
- 可追溯性:测试失败能立即提供可归因的反馈
集成点
与 DDD 工作流的集成
TDD 和 DDD 相辅相成:
- 新代码使用 TDD
- 现有代码重构使用 DDD
- 混合模式结合两种方法
与测试工作流的集成
TDD 与测试工作流集成:
- 使用规格测试
- 集成覆盖率工具
- 支持变异测试,以评估测试质量
与质量框架的集成
TDD 的输出用于质量评估:
- 跟踪覆盖率指标
- 对变更进行 TRUST 5 验证
- 通过质量门禁强制执行标准
故障排除
常见问题
测试过于复杂:
- 拆分为更小、更专注的测试
- 一次测试一个行为
- 对复杂的初始化使用测试固件
实现增长过快:
- 克制实现未经测试功能的冲动
- 为新功能返回 RED 阶段
- 保持 GREEN 阶段的实现精简
重构导致测试失败:
- 立即回滚
- 将重构拆分为更小的步骤
- 确保测试验证的是行为,而非实现
恢复流程
当 TDD 规范执行中断时:
- 停下来评估当前状态
- 为现有代码编写特征测试
- 对剩余功能恢复 TDD
- 考虑切换到混合模式
版本:1.0.0 状态:启用
常见借口
| 借口 | 事实 |
|---|---|
| “等实现可用了再补测试” | 事后编写的测试验证的是代码实际做了什么,而不是代码应该做什么。它们会漏掉 RED 阶段能捕获的错误。 |
| “现有测试已覆盖这种情况” | 现有测试验证的是旧行为。新行为需要先有一个会失败的测试。 |
| “这个函数太简单,不值得测试” | 简单函数也会逐渐变复杂。测试能在行为偏离预期之前记录预期行为。 |
| “我在终端里手动测过了” | 手动检查不会留存。明天的改动可能会破坏约定,却没有任何提示。 |
| “这个测试需要复杂的模拟” | 如果测试很难写,代码就很难推理。先重构设计。 |
| “测试会拖慢我的进度” | 先写测试能尽早发现设计问题,此时修复成本最低。 |
| “为了继续推进,这一轮先跳过 REFACTOR” | 跳过重构会累积问题。下一轮会从更糟糕的基线开始。 |
DAMP 优于 DRY:在测试代码中,优先使用描述清晰且有意义的表达,而不是一味追求不要重复。测试中能让意图一目了然的重复,胜过会隐藏意图的抽象。
Beyonce 规则:如果你喜欢它,就应该为它写个测试。CI 不验证的任何行为,最终都会在没有预警的情况下出错。
危险信号
- 实现文件和测试文件在同一个提交中创建(跳过了 RED 阶段)
- 测试名称描述实现(test_function_returns_true),而不是行为(test_user_login_rejects_expired_token)
- 新文件中的所有测试第一次运行就通过了——没有记录 RED 阶段
- 功能已合并后,提交消息才写“添加测试”
- 测试套件中包含对被测代码的模拟(模拟实现,而不是协作者)
- 添加新功能的提交导致覆盖率下降
验证
- Git 历史显示,实现提交之前有一个测试失败的提交,或同一提交中有 RED 阶段的证据
- 测试名称读起来像行为规范,而不是函数说明
- 每个新增的公共函数至少有一个对应的测试用例
- 完整测试套件通过(粘贴命令输出)
- 已测量并报告变更文件的覆盖率(展示工具输出)
- 此次变更没有新增
skip、xit或已禁用的测试 - 已执行 REFACTOR 阶段,或明确说明无需执行
测试优先反作弊
[ZONE:Evolvable] [HARD] 上述危险信号和验证清单位于建议性(可演进)区块中,不受任何完成门禁检查。本节将其提升为两项强制不变量,使测试优先在完成矩阵中可验证。
- 不变量 i — RED 失败输出是必需的完成证据。 必须观察并展示 RED 阶段失败测试的原始输出(即在任何实现使测试通过之前捕获的失败测试运行),将其作为运行阶段完成证据的一部分。无法生成此输出的运行跳过了 RED 阶段,不能报告为通过。
- 不变量 ii — 先于失败测试编写的实现必须删除,并按测试优先重新推导。 任何在对应失败测试出现之前编写的实现代码,都必须删除,并从失败测试开始重新推导(先 RED,再 GREEN)。“我已经写好了实现,所以测试第一次运行就通过了”正是不变量禁止的确切失败模式——这是测试后置,而不是测试优先;它也没有可展示的 RED 输出(不变量 i)作为证据。
这两个不变量弥补了可证伪性缺口:在此之前,即使没有任何 RED 证据,也能生成完全通过的自验证矩阵,因此无法根据完成报告区分测试优先和测试后置。现在,矩阵要求提供 GREEN 之前 RED 阶段的原始输出,因此跳过 RED 阶段的运行无法提供此类输出,矩阵在结构上也就不完整。