第3章:拍卖应用(The Auction Application)
一、导读
1.1 本章学习目标
第3章以一个在线拍卖应用为主线,展示如何用 TDD 的方式从零开始构建一个真实的系统。
1.2 为什么第三章如此重要
理论需要实践来验证。通过一个完整的实战案例,你可以看到 TDD 如何在真实项目中发挥作用。
1.3 核心问题引导
如何从零开始用 TDD 构建一个系统?
领域模型如何驱动设计?
事件驱动架构如何在 TDD 中体现?
如何处理外部依赖?
二、核心概念详解
3.1 拍卖系统概述
核心领域:
- 拍卖品(Item):被拍卖的物品
- 出价(Bid):竞拍者的出价
- 竞拍者(Bidder):参与竞拍的人
- 拍卖行(AuctionHouse):管理拍卖
事件流:
- 拍卖开始 → 出价 → 出价被接受/拒绝 → 拍卖结束3.2 TDD 驱动设计
java
// 第1步:写验收测试
@Test
public void bidderReceivesNotificationWhenOutbid() {
// 设置拍卖环境
// 竞拍者出价
// 另一个竞拍者出价更高
// 验证第一个竞拍者收到通知
}
// 第2步:写单元测试
@Test
public void shouldNotifyWhenOutbid() {
Auction auction = mock(Auction.class);
AuctionSniper sniper = new AuctionSniper(auction);
sniper.currentPrice(100);
sniper.bid(110);
verify(auction).bid(110);
}3.3 事件驱动
java
// 事件定义
public interface AuctionEvent {
String itemId();
}
public class PriceChanged implements AuctionEvent {
private final String itemId;
private final int price;
// ...
}
// 事件处理
public class AuctionSniper implements AuctionEventListener {
@Override
public void priceChanged(int newPrice) {
if (shouldBid(newPrice)) {
auction.bid(newPrice + increment);
}
}
}三、实战案例
3.1 完整的 TDD 循环
java
// 红灯
@Test
public void sniperBidsWhenPriceIsBelowStopPrice() {
Auction auction = mock(Auction.class);
AuctionSniper sniper = new AuctionSniper(auction, 200);
sniper.priceChanged(100);
verify(auction).bid(110);
}
// 绿灯
public class AuctionSniper implements AuctionEventListener {
private final Auction auction;
private final int stopPrice;
public AuctionSniper(Auction auction, int stopPrice) {
this.auction = auction;
this.stopPrice = stopPrice;
}
@Override
public void priceChanged(int newPrice) {
if (newPrice < stopPrice) {
auction.bid(newPrice + 10);
}
}
}
// 重构
// 提取常量、改善命名、简化逻辑四、本章小结
通过拍卖应用案例,展示了 TDD 如何在真实项目中驱动设计。从验收测试到单元测试,从领域模型到事件驱动,TDD 贯穿整个开发过程。
五、思考与练习
如何确定第一个测试?
如何处理外部依赖(如 XMPP 协议)?
如何从测试中提取领域模型?
TDD 如何帮助发现设计问题?
如何平衡测试数量和开发速度?