本文目录导读:

- 案例需求:实现一个购物车类(ShoppingCart)
- 第一步:编写失败的测试(红灯)
- 第二步:编写最少代码让测试通过(绿灯)
- 第三步:重构(Refactor)
- 测试运行结果
- TDD 核心三步骤回顾
- 什么时候用 TDD?
下面我给你一个完整的 TDD(测试驱动开发) 实战案例,我会带你走一遍完整的 红-绿-重构 循环。
案例需求:实现一个购物车类(ShoppingCart)
功能要求:
- 可以添加商品(商品有名称和单价)
- 可以移除商品
- 可以计算总价(支持折扣逻辑:满 100 减 10)
- 可以获取商品数量
第一步:编写失败的测试(红灯)
先写测试,再写实现代码。
// shoppingCart.test.ts
import { ShoppingCart } from './shoppingCart';
describe('ShoppingCart', () => {
let cart: ShoppingCart;
beforeEach(() => {
cart = new ShoppingCart();
});
describe('添加商品', () => {
test('空购物车添加一个商品后,商品数量为 1', () => {
cart.addItem({ name: '苹果', price: 5 });
expect(cart.getItemCount()).toBe(1);
});
test('添加多个相同商品后,数量正确', () => {
cart.addItem({ name: '苹果', price: 5 });
cart.addItem({ name: '苹果', price: 5 });
cart.addItem({ name: '香蕉', price: 3 });
expect(cart.getItemCount()).toBe(3);
});
});
describe('移除商品', () => {
test('移除一个商品后,数量减少', () => {
cart.addItem({ name: '苹果', price: 5 });
cart.addItem({ name: '香蕉', price: 3 });
cart.removeItem('苹果');
expect(cart.getItemCount()).toBe(1);
});
});
describe('计算总价', () => {
test('没有折扣时,总价正确', () => {
cart.addItem({ name: '苹果', price: 5 });
cart.addItem({ name: '香蕉', price: 10 });
expect(cart.getTotalPrice()).toBe(15);
});
test('总价达到 100 元,享受满 100 减 10 优惠', () => {
// 10 个苹果,每个 10 元 = 100 元
for (let i = 0; i < 10; i++) {
cart.addItem({ name: '苹果', price: 10 });
}
expect(cart.getTotalPrice()).toBe(90);
});
test('总价未满 100 元,不享受优惠', () => {
cart.addItem({ name: '苹果', price: 99 });
expect(cart.getTotalPrice()).toBe(99);
});
});
});
⚠️ 此时运行测试,会报错:
Cannot find module './shoppingCart'— 这就是 红灯。
第二步:编写最少代码让测试通过(绿灯)
// shoppingCart.ts
export interface Item {
name: string;
price: number;
}
export class ShoppingCart {
private items: Item[] = [];
addItem(item: Item): void {
this.items.push(item);
}
removeItem(name: string): void {
this.items = this.items.filter(item => item.name !== name);
}
getItemCount(): number {
return this.items.length;
}
getTotalPrice(): number {
const total = this.items.reduce((sum, item) => sum + item.price, 0);
// 满 100 减 10
return total >= 100 ? total - 10 : total;
}
}
✅ 此时运行所有测试,全部通过 — 这就是 绿灯。
第三步:重构(Refactor)
重构阶段不能改变功能,只优化代码结构和可读性。
// 重构后的代码(逻辑不变,代码更优雅)
export class ShoppingCart {
private items: Item[] = [];
addItem(item: Item): void {
this.items.push(item);
}
removeItem(name: string): void {
this.items = this.items.filter(item => item.name !== name);
}
getItemCount(): number {
return this.items.length;
}
getTotalPrice(): number {
const total = this.getRawTotal();
return this.applyDiscount(total);
}
private getRawTotal(): number {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
private applyDiscount(total: number): number {
const DISCOUNT_THRESHOLD = 100;
const DISCOUNT_AMOUNT = 10;
return total >= DISCOUNT_THRESHOLD ? total - DISCOUNT_AMOUNT : total;
}
}
✅ 重构后再次运行测试,确认仍然全部通过。
测试运行结果
✓ 空购物车添加一个商品后,商品数量为 1
✓ 添加多个相同商品后,数量正确
✓ 移除一个商品后,数量减少
✓ 没有折扣时,总价正确
✓ 总价达到 100 元,享受满 100 减 10 优惠
✓ 总价未满 100 元,不享受优惠
Test Suites: 1 passed, 1 total
Tests: 6 passed, 6 total
TDD 核心三步骤回顾
| 阶段 | 行动 | 说明 |
|---|---|---|
| 🔴 红灯 | 先写失败的测试 | 验证测试框架能捕获缺失功能 |
| 🟢 绿灯 | 写最少代码让测试通过 | 不要写多余代码 |
| 🔵 重构 | 优化代码结构,保持功能不变 | 提高代码质量,测试保证安全 |
什么时候用 TDD?
- 逻辑复杂、容易出错的业务代码(如价格计算)
- 需要长期维护的核心模块
- 有明确输入输出的功能
注意: TDD 不适用于所有场景(如 UI 界面、原型探索期),要灵活运用。
需要我再给你一个 带边界条件测试 或 异步逻辑(如 API 调用) 的 TDD 案例吗?
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播