跳至正文

🧪 测试驱动开发(TDD)

📖 定义

TDD(测试驱动开发)是一种先编写测试,然后编写代码以通过这些测试的开发方法。它遵循红-绿-重构循环,提高代码质量,减少错误,使重构更安全。单元测试独立验证单个函数或组件。

🎯 简单类比

先画蓝图

传统开发
1. 开始建房子
2. 完成后检查
3. 发现问题 → 需要大修改
4. 成本增加

TDD
1. 画蓝图(编写测试)
2. 按蓝图建造(编写代码)
3. 验证(运行测试)
4. 改进(重构)
5. 安全准确

⚙️ 工作原理

TDD循环 (红-绿-重构)

🔴 红(失败)
└─ 编写测试 → 失败(无代码)

🟢 绿(通过)
└─ 编写最少代码 → 测试通过

🔵 重构(改进)
└─ 改进代码 → 测试仍然通过

重复 → 逐步改进

💡 关键示例

基本TDD示例

// ========== 1. 红: 编写测试(失败) ==========
test('add函数相加两个数字', () => {
expect(add(2, 3)).toBe(5);
});
// FAIL - add未定义

// ========== 2. 绿: 最少代码(通过) ==========
function add(a, b) {
return a + b;
}
// PASS ✅

// ========== 3. 重构: 改进(如需要) ==========
// 代码简单,无需改进

Jest单元测试

// user.js
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}

isValidEmail() {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(this.email);
}
}

// user.test.js
describe('User', () => {
let user;

beforeEach(() => {
user = new User('张三', 'zhang@example.com');
});

test('有效邮箱应返回true', () => {
expect(user.isValidEmail()).toBe(true);
});

test('无效邮箱应返回false', () => {
user.email = 'invalid-email';
expect(user.isValidEmail()).toBe(false);
});
});

异步测试

test('异步函数返回数据', async () => {
const data = await fetchData();
expect(data).toEqual({ name: 'John' });
});

模拟

// 函数模拟
const mockCallback = jest.fn(x => x * 2);

[1, 2, 3].forEach(mockCallback);

expect(mockCallback).toHaveBeenCalledTimes(3);

// 模块模拟
jest.mock('axios');

test('fetchUser返回用户数据', async () => {
axios.get.mockResolvedValue({ data: { id: 1 } });
const user = await fetchUser(1);
expect(user).toEqual({ id: 1 });
});

🤔 常见问题

Q1. TDD的优点?

A:

优点:
1. 减少错误
2. 安全重构
3. 代码质量提高
4. 文档化
5. 信心

缺点:
1. 初始时间投资
2. 学习曲线
3. 测试维护

结论: 长期收益

Q2. 应该测试什么?

A:

// ✅ 应该测试
1. 业务逻辑
2. 边缘情况
3. 错误处理
4. 公共API

// ❌ 不需要测试
1. 外部库
2. 简单getter/setter
3. 私有实现细节

Q3. 测试覆盖率目标?

A:

# 测量覆盖率
npm test -- --coverage

# 目标:
80%以上覆盖率 // 现实目标
100%覆盖率 // 理想但不现实

# 记住:
高覆盖率 ≠ 好测试
有意义的测试很重要!

🎬 总结

TDD是软件质量的基础:

  • 红-绿-重构: TDD核心循环
  • 单元测试: 快速且隔离
  • 测试优先: 测试驱动设计
  • 持续改进: 重构的安全网

测试是对未来自己的投资! 🧪✨