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: 用于安全测试
工作流总结
- 红: 编写失败测试
- 绿: 实现最小代码
- 重构: 改进同时保持测试通过
- 验证: 检查覆盖满足要求
- 提交: 仅在所有测试通过时提交
绝不跳过步骤。绝不提交未测试代码。
相关技能
补充技能(一起使用)
- testing-patterns - 单元、集成和E2E测试的全面测试模式
- verification-loop - 预提交验证工作流,验证所有质量门
- deployment-cicd - CI管道设置用于自动测试执行
替代技能(类似目的)
- 无 - TDD是一种特定方法论,补充其他测试方法
先决技能(先学习)
- testing-patterns - 理解测试类型和框架有助于TDD