mcpField
M
moai-workflow-tdd

moai-workflow-tdd

github.com/modu-ai测试与质量1.2k55 次安装今天更新
代码仓库

关于 moai-workflow-tdd

moai-workflow-tdd 是面向新功能开发的测试驱动开发技能,将工作组织为 RED-GREEN-REFACTOR 循环,先用失败测试定义行为,再编写实现。

主要功能

  1. 01RED-GREEN-REFACTOR 开发循环
  2. 02实现前编写行为测试
  3. 03以通过测试为目标的最小实现
  4. 04持续运行测试验证重构
  5. 05覆盖率与质量验证

适用场景

  1. 01依据契约开发新的 API 端点
  2. 02构建行为定义明确的 UI 组件
  3. 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 阶段的运行无法提供此类输出,矩阵在结构上也就不完整。

评论

支持 Markdown