章節概述#

本章的目標是讓待辦清單上的 5 CHF * 2 = 10 CHF 測試通過。這看起來是往混合貨幣運算邁進的必要前提——我們需要一個類似 Dollar 的 Franc 物件。Kent Beck 在本章坦率地承認:為了速度,他選擇了複製貼上

從小測試開始#

待辦清單上最有趣的項目(混合貨幣加法)跨度太大,無法一步到位。因此,先退一步,建立一個更小的測試作為踏腳石:

public void testFrancMultiplication() {
    Franc five = new Franc(5);
    assertEquals(new Franc(10), five.times(2));
    assertEquals(new Franc(15), five.times(3));
}

這個測試直接複製自 Dollar 的測試,只是把 Dollar 換成了 Franc。(作者也提到,前一章簡化測試的成果在這裡立刻發揮了作用。)

用複製貼上快速通過測試#

讓這個測試通過的最短路徑是什麼?直接把 Dollar 的程式碼複製一份,把名字改成 Franc:

class Franc {
    private int amount;

    Franc(int amount) {
        this.amount = amount;
    }

    Franc times(int multiplier) {
        return new Franc(amount * multiplier);
    }

    public boolean equals(Object object) {
        Franc franc = (Franc) object;
        return amount == franc.amount;
    }
}

測試立刻通過,甚至連「讓它編譯通過」這一步都省了。

TDD 循環的五個階段#

作者預料到讀者對複製貼上的不滿,特別強調了 TDD 循環的完整五步

  1. 寫一個測試
  2. 讓它編譯通過
  3. 執行測試,確認它失敗
  4. 讓它通過
  5. 消除重複

前四步追求的是速度——盡快進入已知狀態、確認新功能可行。在這個階段,任何「罪過」都可以忍受。但循環必須走完第五步:消除重複(Remove duplication)

重點: 「Make it run, make it right.」——先讓它能跑,再讓它正確。四條腿的 Aeron 椅子會倒。沒有第五步,前四步就不完整。好的設計在對的時機出現。

更新後的待辦清單#

通過測試後,清單上劃掉了 5 CHF * 2 = 10 CHF,但也新增了兩項技術債:

  • 5 CHF * 2 = 10 CHF
  • Dollar/Franc duplication(新增)
  • Common equals(新增)
  • Common times(新增)

本章小結#

  • 面對太大的測試時,發明一個較小的、代表進展的測試
  • 用無恥的複製貼上來撰寫測試
  • 更過分地,直接批量複製模型程式碼來讓測試通過
  • 承諾在消除重複之前絕不收工