章節概述#
本章利用上一章完成的 equals() 功能來改善測試的表達力,進而將 amount 欄位設為 private。這是一個典型的 TDD 模式:新完成的功能立即被用來改善既有的程式碼與測試。
用 equals() 改善測試#
有了 equality 之後,Dollar.times() 的測試可以更具表達力。概念上,times() 應該回傳一個值為「接收者乘以乘數」的 Dollar。但原本的測試沒有直接表達這件事:
public void testMultiplication() {
Dollar five = new Dollar(5);
Dollar product = five.times(2);
assertEquals(10, product.amount);
product = five.times(3);
assertEquals(15, product.amount);
}第一步:用 Dollar 比較取代整數比較#
public void testMultiplication() {
Dollar five = new Dollar(5);
Dollar product = five.times(2);
assertEquals(new Dollar(10), product);
product = five.times(3);
assertEquals(new Dollar(15), product);
}第二步:內聯暫存變數#
product 變數已經不再有太大幫助,可以直接內聯:
public void testMultiplication() {
Dollar five = new Dollar(5);
assertEquals(new Dollar(10), five.times(2));
assertEquals(new Dollar(15), five.times(3));
}技巧: 改寫後的測試讀起來更像是一個真理的斷言(assertion of truth),而非一連串的操作步驟。好的測試應該清楚地「說出」它在驗證什麼。
將 amount 設為 private#
經過上述修改,測試不再直接存取 product.amount,而是透過 equals() 來比較。現在只剩 Dollar 類別本身使用 amount 欄位,因此可以安心地將它設為 private:
private int amount;待辦清單上的「Make amount private」可以劃掉了。
風險管理:測試之間的耦合#
注意: 這裡引入了一個風險——如果
equals()的測試未能準確地驗證相等性是否正確運作,那麼times()的測試也可能無法準確驗證乘法是否正確。這是 TDD 中我們主動管理的風險。
我們不追求完美。透過用兩種方式表達每件事——程式碼和測試——我們希望將缺陷減少到足以讓我們有信心前進的程度。偶爾我們的推理會失誤,缺陷會溜過去。發生時,我們從中學到應該撰寫的測試,然後繼續前進。其餘時間,我們在飄揚的綠燈旗下勇敢前行。
本章小結#
本章的要點回顧:
- 利用剛完成的功能來改善測試——用
equals()讓測試更具表達力 - 注意到如果兩個測試同時失敗,就無法準確定位問題
- 儘管存在風險,仍然決定繼續推進
- 利用被測物件的新功能來降低測試與程式碼之間的耦合