测试驱动开发工作流 tdd-workflow

测试驱动开发(TDD)工作流技能,用于在软件开发中强制执行先写测试再写代码的原则,确保高测试覆盖率和代码质量。关键词:测试驱动开发、TDD、测试覆盖、红绿重构、单元测试、集成测试、端到端测试、软件开发测试。

测试 0 次安装 0 次浏览 更新于 3/7/2026

name: tdd-workflow description: 测试驱动开发工作流,包含单元测试、集成测试和端到端测试的全面覆盖要求 license: MIT complexity: 中级 time_to_learn: 30分钟 prerequisites:

  • testing-patterns tags:
  • tdd
  • test-driven-development
  • red-green-refactor
  • coverage
  • quality inputs:
  • 功能需求
  • 源代码 outputs:
  • 测试套件
  • 实现代码 side_effects:
  • 创建文件
  • 运行命令 triggers:
  • 用户询问关于tdd
  • 用户询问关于测试
  • 文件类型:*.test.ts
  • 文件类型:*.spec.py complements:
  • testing-patterns
  • verification-loop tier: 核心 metadata: source: affaan-m/everything-claude-code adapted-by: ai-skills category: 开发工作流

测试驱动开发工作流

强制执行测试驱动开发原则,跨所有层实现全面的测试覆盖。

何时激活

  • 编写新功能或功能
  • 修复错误或问题
  • 重构现有代码
  • 添加API端点
  • 创建新组件
  • 实现业务逻辑

核心原则

1. 测试先于代码

始终先写测试,然后实现代码使测试通过。没有例外。

2. 覆盖要求

  • 最小80%覆盖率(单元 + 集成 + E2E)
  • 覆盖所有边缘情况
  • 测试错误场景
  • 验证边界条件
  • 快乐路径和悲伤路径

3. 测试金字塔

        ┌────────┐
        │   E2E  │  (10-20%)
        ├────────┤
        │  集成  │  (20-30%)
        ├────────┤
        │  单元  │  (50-70%)
        └────────┘

TDD 工作流:红-绿-重构

步骤 1: 红 - 编写失败测试

从用户故事开始:

作为一个[角色],我想要[动作],以便[收益]

示例:
作为一个用户,我想要按类别搜索产品,
以便我能快速找到相关物品。

编写测试:

describe('产品搜索', () => {
  it('返回按类别过滤的产品', async () => {
    const products = await searchProducts({ category: '电子产品' });
    
    expect(products).toHaveLength(5);
    expect(products.every(p => p.category === '电子产品')).toBe(true);
  });
});

步骤 2: 运行测试(它们应该失败)

npm test
# ✗ 产品搜索 > 返回按类别过滤的产品
#   TypeError: searchProducts 不是一个函数

这很好! 红阶段确认测试在工作。

步骤 3: 绿 - 实现最小代码

编写足够的代码来通过:

export async function searchProducts(filters: SearchFilters) {
  const { category } = filters;
  return db.products.findMany({
    where: { category }
  });
}

步骤 4: 运行测试(它们应该通过)

npm test
# ✓ 产品搜索 > 返回按类别过滤的产品 (42ms)

步骤 5: 重构 - 改进代码

现在重构,同时保持测试通过:

export async function searchProducts(filters: SearchFilters) {
  const query = buildSearchQuery(filters);
  const results = await executeSearch(query);
  return transformResults(results);
}

再次运行测试以确保它们仍然通过。

测试类型

单元测试

目的: 隔离测试单个函数

describe('计算折扣', () => {
  it('为基本层级应用10%折扣', () => {
    const price = calculateDiscount(100, 'basic');
    expect(price).toBe(90);
  });

  it('为高级层级应用20%折扣', () => {
    const price = calculateDiscount(100, 'premium');
    expect(price).toBe(80);
  });

  it('为无效层级抛出错误', () => {
    expect(() => calculateDiscount(100, 'invalid')).toThrow();
  });
});

集成测试

目的: 测试多个组件一起工作

describe('用户注册 API', () => {
  it('创建用户并发送欢迎邮件', async () => {
    const response = await request(app)
      .post('/api/register')
      .send({ email: 'test@example.com', password: 'secret' });  // allow-secret

    expect(response.status).toBe(201);
    expect(response.body.user.email).toBe('test@example.com');
    
    // 验证邮件已发送
    const sentEmails = await testEmailService.getSentEmails();
    expect(sentEmails).toHaveLength(1);
    expect(sentEmails[0].to).toBe('test@example.com');
  });
});

E2E 测试 (Playwright/Cypress)

目的: 通过UI测试完整的用户流程

test('用户可以完成结账过程', async ({ page }) => {
  await page.goto('/products');
  await page.click('[data-testid="add-to-cart"]');
  await page.click('[data-testid="cart"]');
  await page.click('[data-testid="checkout"]');
  await page.fill('[name="cardNumber"]', '4242424242424242');
  await page.click('[data-testid="complete-order"]');

  await expect(page.locator('.success-message')).toBeVisible();
  await expect(page).toHaveURL(/\/order-confirmation/);
});

覆盖验证

实现后,检查覆盖:

npm test -- --coverage

# 输出:
# 文件          | % 语句 | % 分支 | % 函数 | % 行
# --------------|---------|----------|---------|--------
# 所有文件     |   84.2  |   78.5   |   91.3  |   85.1

要求:

  • 语句: > 80%
  • 分支: > 75%
  • 函数: > 85%
  • 行: > 80%

边缘情况检查列表

始终测试:

  • [ ] 空输入
  • [ ] 空值/未定义值
  • [ ] 大数据集
  • [ ] 并发操作
  • [ ] 网络故障
  • [ ] 数据库错误
  • [ ] 无效数据类型
  • [ ] 边界值 (0, -1, MAX_INT)
  • [ ] 未授权访问
  • [ ] 速率限制

模拟策略

何时模拟

  • 外部API
  • 数据库调用(在单元测试中)
  • 时间相关函数
  • 文件系统操作
  • 第三方服务

示例模拟

// 模拟外部服务
vi.mock('./emailService', () => ({
  sendEmail: vi.fn().mockResolvedValue({ success: true })
}));

// 模拟数据库
vi.mock('./db', () => ({
  users: {
    findUnique: vi.fn().mockResolvedValue({ id: 1, name: '测试' })
  }
}));

// 模拟 Date.now()
vi.spyOn(Date, 'now').mockReturnValue(1234567890000);

测试组织

src/
  features/
    products/
      product.service.ts
      product.service.test.ts      # 单元测试
      product.integration.test.ts  # 集成测试
      product.e2e.test.ts          # E2E测试

调试失败测试

# 运行单个测试文件
npm test -- product.service.test.ts

# 运行单个测试
npm test -- -t "正确计算折扣"

# 运行监视模式
npm test -- --watch

# 运行覆盖
npm test -- --coverage --no-cache

集成点

补充:

  • verification-loop: 用于预提交验证
  • testing-patterns: 用于测试设计模式
  • deployment-cicd: 用于CI测试执行
  • security-implementation-guide: 用于安全测试

工作流总结

  1. : 编写失败测试
  2. 绿: 实现最小代码
  3. 重构: 改进同时保持测试通过
  4. 验证: 检查覆盖满足要求
  5. 提交: 仅在所有测试通过时提交

绝不跳过步骤。绝不提交未测试代码。


相关技能

补充技能(一起使用)

替代技能(类似目的)

  • 无 - TDD是一种特定方法论,补充其他测试方法

先决技能(先学习)