Files
everything-claude-code-zh/commands/tdd.md

8.4 KiB
Raw Blame History

description
description
Enforce test-driven development workflow. Scaffold interfaces, generate tests FIRST, then implement minimal code to pass. Ensure 80%+ coverage.

TDD 命令

此命令调用 tdd-guide 智能体Agent来强制执行测试驱动开发TDD方法论。

此命令的作用

  1. 搭建接口Scaffold Interfaces - 首先定义类型/接口
  2. 先生成测试 - 编写失败的测试(红/RED
  3. 编写最小化实现代码 - 只编写刚好能通过测试的代码(绿/GREEN
  4. 重构Refactor - 在保持测试通过的前提下优化代码(重构/REFACTOR
  5. 验证覆盖率 - 确保测试覆盖率达到 80% 以上

适用场景

在以下情况下使用 /tdd

  • 实现新功能
  • 添加新的函数/组件
  • 修复 Bug先编写重现该 Bug 的测试)
  • 重构现有代码
  • 构建核心业务逻辑

工作原理

tdd-guide 智能体将:

  1. 为输入/输出定义接口
  2. 编写会失败FAIL的测试(因为代码尚未存在)
  3. 运行测试并验证它们因预期的原因而失败
  4. 编写最小化实现以使测试通过
  5. 运行测试并验证它们通过
  6. 在保持测试通过的前提下重构代码
  7. 检查覆盖率,如果低于 80% 则添加更多测试

TDD 循环

红 (RED) → 绿 (GREEN) → 重构 (REFACTOR) → 重复 (REPEAT)

红 (RED):      编写一个失败的测试
绿 (GREEN):    编写最小化代码以通过测试
重构 (REFACTOR): 改进代码,保持测试通过
重复 (REPEAT):   下一个功能/场景

使用示例

User: /tdd 我需要一个计算市场流动性评分market liquidity score的函数

Agent (tdd-guide):
# TDD 会话:市场流动性评分计算器

## 步骤 1定义接口 (SCAFFOLD)

```typescript
// lib/liquidity.ts
export interface MarketData {
  totalVolume: number
  bidAskSpread: number
  activeTraders: number
  lastTradeTime: Date
}

export function calculateLiquidityScore(market: MarketData): number {
  // TODO: 待实现
  throw new Error('Not implemented')
}

步骤 2编写失败的测试 (RED)

// lib/liquidity.test.ts
import { calculateLiquidityScore } from './liquidity'

describe('calculateLiquidityScore', () => {
  it('should return high score for liquid market', () => {
    const market = {
      totalVolume: 100000,
      bidAskSpread: 0.01,
      activeTraders: 500,
      lastTradeTime: new Date()
    }

    const score = calculateLiquidityScore(market)

    expect(score).toBeGreaterThan(80)
    expect(score).toBeLessThanOrEqual(100)
  })

  it('should return low score for illiquid market', () => {
    const market = {
      totalVolume: 100,
      bidAskSpread: 0.5,
      activeTraders: 2,
      lastTradeTime: new Date(Date.now() - 86400000) // 1 天前
    }

    const score = calculateLiquidityScore(market)

    expect(score).toBeLessThan(30)
    expect(score).toBeGreaterThanOrEqual(0)
  })

  it('should handle edge case: zero volume', () => {
    const market = {
      totalVolume: 0,
      bidAskSpread: 0,
      activeTraders: 0,
      lastTradeTime: new Date()
    }

    const score = calculateLiquidityScore(market)

    expect(score).toBe(0)
  })
})

步骤 3运行测试 - 验证失败 (FAIL)

npm test lib/liquidity.test.ts

FAIL lib/liquidity.test.ts
  ✕ should return high score for liquid market (2 ms)
    Error: Not implemented

1 test failed, 0 passed

测试如预期般失败。准备开始实现。

步骤 4编写最小化实现代码 (GREEN)

// lib/liquidity.ts
export function calculateLiquidityScore(market: MarketData): number {
  // 处理零交易量的边缘情况
  if (market.totalVolume === 0) {
    return 0
  }

  // 计算各项评分0-100 刻度)
  const volumeScore = Math.min(market.totalVolume / 1000, 100)
  const spreadScore = Math.max(100 - (market.bidAskSpread * 1000), 0)
  const traderScore = Math.min(market.activeTraders / 10, 100)

  // 最近活跃度奖励
  const hoursSinceLastTrade = (Date.now() - market.lastTradeTime.getTime()) / (1000 * 60 * 60)
  const recencyScore = Math.max(100 - (hoursSinceLastTrade * 10), 0)

  // 加权平均
  const score = (
    volumeScore * 0.4 +
    spreadScore * 0.3 +
    traderScore * 0.2 +
    recencyScore * 0.1
  )

  return Math.min(Math.max(score, 0), 100) // 限制在 0-100 之间
}

步骤 5运行测试 - 验证通过 (PASS)

npm test lib/liquidity.test.ts

PASS lib/liquidity.test.ts
  ✓ should return high score for liquid market (3 ms)
  ✓ should return low score for illiquid market (2 ms)
  ✓ should handle edge case: zero volume (1 ms)

3 tests passed

所有测试均已通过!

步骤 6重构 (IMPROVE)

// lib/liquidity.ts - 使用常量重构以提高可读性
const WEIGHTS = {
  VOLUME: 0.4,
  SPREAD: 0.3,
  TRADERS: 0.2,
  RECENCY: 0.1,
} as const

const SCALE_FACTORS = {
  VOLUME: 1000,
  SPREAD: 1000,
  TRADERS: 10,
  RECENCY_PENALTY: 10,
} as const

function clamp(value: number, min: number, max: number): number {
  return Math.min(Math.max(value, min), max)
}

export function calculateLiquidityScore(market: MarketData): number {
  if (market.totalVolume === 0) return 0

  const volumeScore = Math.min(market.totalVolume / SCALE_FACTORS.VOLUME, 100)
  const spreadScore = clamp(100 - (market.bidAskSpread * SCALE_FACTORS.SPREAD), 0, 100)
  const traderScore = Math.min(market.activeTraders / SCALE_FACTORS.TRADERS, 100)

  const hoursSinceLastTrade = (Date.now() - market.lastTradeTime.getTime()) / (1000 * 60 * 60)
  const recencyScore = clamp(100 - (hoursSinceLastTrade * SCALE_FACTORS.RECENCY_PENALTY), 0, 100)

  const weightedScore =
    volumeScore * WEIGHTS.VOLUME +
    spreadScore * WEIGHTS.SPREAD +
    traderScore * WEIGHTS.TRADERS +
    recencyScore * WEIGHTS.RECENCY

  return clamp(weightedScore, 0, 100)
}

步骤 7验证测试依然通过

npm test lib/liquidity.test.ts

PASS lib/liquidity.test.ts
  ✓ should return high score for liquid market (3 ms)
  ✓ should return low score for illiquid market (2 ms)
  ✓ should handle edge case: zero volume (1 ms)

3 tests passed

重构完成,测试依然通过!

步骤 8检查覆盖率

npm test -- --coverage lib/liquidity.test.ts

File           | % Stmts | % Branch | % Funcs | % Lines
---------------|---------|----------|---------|--------
liquidity.ts   |   100   |   100    |   100   |   100

Coverage: 100% ✅ (目标: 80%)

TDD 会话完成!


## TDD 最佳实践

**建议做法:**
- ✅ **先**编写测试,在进行任何实现之前
- ✅ 在编写实现之前,先运行测试并验证它们**失败**
- ✅ 编写最小化代码以使测试通过
- ✅ 仅在测试通过(绿色)后才进行重构
- ✅ 涵盖边缘情况和错误场景
- ✅ 目标是 80% 以上的覆盖率(核心代码要求 100%

**避免做法:**
- ❌ 在测试之前编写实现
- ❌ 每次更改后跳过运行测试
- ❌ 一次性编写过多代码
- ❌ 忽略失败的测试
- ❌ 测试实现细节(应测试行为)
- ❌ 模拟Mock一切更推荐集成测试

## 应包含的测试类型

**单元测试** (函数级别):
- 核心流程Happy path场景
- 边缘情况空值、null、最大值
- 错误条件
- 边界值

**集成测试** (组件级别):
- API 端点
- 数据库操作
- 外部服务调用
- 带有 Hooks 的 React 组件

**E2E 测试** (使用 `/e2e` 命令):
- 关键用户流程
- 多步骤流程
- 全栈集成

## 覆盖率要求

- 所有代码**最低 80%**
- 以下内容**要求 100%**
  - 财务计算
  - 身份验证逻辑
  - 关键安全代码
  - 核心业务逻辑

## 重要提示

**强制性**必须在实现之前编写测试。TDD 循环是:

1.  **红 (RED)** - 编写失败的测试
2.  **绿 (GREEN)** - 实现代码以通过测试
3.  **重构 (REFACTOR)** - 优化代码

切勿跳过红色RED阶段。切勿在测试之前编写代码。

## 与其他命令的集成

- 首先使用 `/plan` 了解要构建的内容
- 使用 `/tdd` 进行带测试的实现
- 如果出现构建错误,使用 `/build-and-fix`
- 使用 `/code-review` 审查实现
- 使用 `/test-coverage` 验证覆盖率

## 相关智能体Agents

此命令调用位于以下位置的 `tdd-guide` 智能体:
`~/.claude/agents/tdd-guide.md`

并可以参考位于以下位置的 `tdd-workflow` 技能Skill
`~/.claude/skills/tdd-workflow/`