Commandgeneral

/tdd Command

使用测试驱动开发方法实现:$ARGUMENTS

View Source

TDD 测试驱动开发

使用测试驱动开发方法实现:$ARGUMENTS

TDD 核心循环

🔴 RED - 编写失败的测试

  1. 理解需求

    • 明确功能的输入和输出
    • 定义边界条件和异常情况
    • 确定成功标准
  2. 编写测试

    // 示例结构
    describe('功能名称', () => {
      it('应该在[条件]时[预期行为]', () => {
        // Arrange - 准备测试数据
        // Act - 执行被测试的功能
        // Assert - 验证结果
      });
    });
    
  3. 验证测试失败

    • 运行测试确保失败(重要!)
    • 失败原因应该是"功能未实现"
    • 不要写任何实现代码

🟢 GREEN - 编写通过测试的代码

  1. 最简实现

    • 写最少的代码让测试通过
    • 不考虑优化和优雅
    • 专注于功能正确性
  2. 运行测试

    • 确保新写的测试通过
    • 确保已有测试仍然通过
    • 如果失败,修复代码(不改测试)
  3. 提交代码

    • 测试和实现一起提交
    • 提交信息说明实现的功能

🔵 REFACTOR - 重构优化

  1. 改善代码结构

    • 消除重复代码
    • 提取函数或常量
    • 改善命名和可读性
  2. 保持测试通过

    • 每次小改动后运行测试
    • 确保功能没有被破坏
    • 可以调整测试结构(不改逻辑)
  3. 性能优化(如需要)

    • 只在确实需要时优化
    • 先测量,后优化
    • 保持代码可读性

执行步骤

步骤 1: 分析需求

  • [ ] 理解要实现的功能
  • [ ] 列出所有测试场景
  • [ ] 确定输入输出格式

步骤 2: 编写第一个测试

  • [ ] 选择最简单的场景
  • [ ] 编写清晰的测试描述
  • [ ] 实现测试逻辑
  • [ ] 运行并确认失败

步骤 3: 实现功能

  • [ ] 编写最简代码通过测试
  • [ ] 不添加额外功能
  • [ ] 运行测试确认通过

步骤 4: 添加更多测试

  • [ ] 覆盖正常场景
  • [ ] 覆盖边界条件
  • [ ] 覆盖错误处理
  • [ ] 每个测试重复 RED-GREEN-REFACTOR

步骤 5: 重构和优化

  • [ ] 识别重复代码
  • [ ] 提取共享逻辑
  • [ ] 改善代码结构
  • [ ] 确保所有测试通过

最佳实践

测试命名

// ✅ 好的命名 - 描述行为
it('should return user name when user exists')
it('should throw error when user not found')

// ❌ 差的命名 - 太模糊
it('test user')
it('works correctly')

测试独立性

  • 每个测试独立运行
  • 不依赖其他测试的状态
  • 使用 beforeEach/afterEach 重置状态

测试覆盖

  • 正常路径(Happy Path)
  • 边界条件(空值、极值)
  • 错误情况(异常、无效输入)
  • 并发场景(如适用)

断言原则

// 一个测试一个断言(理想情况)
it('should calculate total price', () => {
  const result = calculateTotal(items);
  expect(result).toBe(100);
});

// 相关的多个断言可以接受
it('should return complete user object', () => {
  const user = getUser(1);
  expect(user.id).toBe(1);
  expect(user.name).toBeDefined();
  expect(user.email).toMatch(/@/);
});

常见错误

❌ 避免这些错误

  1. 跳过 RED 阶段 - 直接写实现代码
  2. 修改测试来通过 - 应该修改实现
  3. 写太多代码 - 超出测试要求的范围
  4. 延迟重构 - 积累技术债务
  5. 测试实现细节 - 应该测试行为

✅ 正确的做法

  1. 严格遵循 RED-GREEN-REFACTOR
  2. 保持测试简单明了
  3. 小步前进,频繁提交
  4. 测试行为,不测试实现
  5. 及时重构,保持代码整洁

示例工作流

# 1. 创建测试文件
touch test/feature.test.js

# 2. 编写第一个失败的测试
npm test -- --watch

# 3. 实现功能让测试通过
# ... 编码 ...

# 4. 运行所有测试
npm test

# 5. 重构代码
# ... 重构 ...

# 6. 确认测试仍然通过
npm test

# 7. 提交代码
git add .
git commit -m "feat: implement feature using TDD"

记住

"TDD 不是关于测试,而是关于设计。通过先写测试,我们被迫思考接口和行为,而不是实现细节。" - Kent Beck

使用 TDD 的目标不仅是编写测试,更是:

  • 🎯 驱动更好的设计
  • 🛡️ 建立信心安全网
  • 📖 创建活文档
  • 🔄 支持持续重构