l

2017年3月7日 星期二

BDD(15)用單元測試驗證類別行為

March 06 14:06~15:20

屏幕截图 2017-03-06 15.42.47

 

昨天談到「開三聯式發票」功能如果只用一個Invoice類別可能會違反單一責任原則進而形成Divergent Change怪味道,今天單純從物件導向設計或是modeling的角度來探討只用一個Invoice類別可能會有什麼問題。

用單元測試紀錄行為

▼先從簡單的開始,如果要開一張空白發票。

屏幕截图 2017-03-06 14.11.44

 

▼執行結果失敗,這個問題和設計無關,只是忘了設定Invoice類別的初始值。

屏幕截图 2017-03-06 14.12.15

 

▼把以下四個屬性初始值都設為0就可以了。

屏幕截图 2017-03-06 14.13.33

 

▼測試案例通過。

屏幕截图 2017-03-06 14.14.25

 

▼寫到這裡想到一個問題:「如果不呼叫issue函數,直接產生一個Invoice物件是否也等於開一張空白發票 ?」寫個測試案例試看看:

屏幕截图 2017-03-06 14.15.38

 

▼測試通過,驗證了剛剛的假設,開始有點懷疑issue函數的必要性。但這也可能只是空白發票的行為剛好一樣,至少還需要再寫一個測試案例反應非空白發票的行為才能確定。

屏幕截图 2017-03-06 14.18.07

***

非空白發票的行為

▼首先回顧一下原本已經有的單元測試:「提供含稅價格來開發票。」

屏幕截图 2017-03-06 14.21.42

 

▼接著試著把一張含稅價格為17000的發票,重設含稅價格為105,看看newInvoice物件是否會重新計算發票內容。

屏幕截图 2017-03-06 14.23.20

 

▼測試結果通過,得知如果要計算稅金與未稅價格只要重設含稅價格即可。因此更加確定在目前的設計中,issue函數應該是多餘的。

屏幕截图 2017-03-06 14.26.01

 

▼最後再寫一個測試案例告訴我們:「不需要透過issue函數開發票,每次重設含稅價格的時候就會重新計算一次發票。」

屏幕截图 2017-03-06 14.30.08

 

測試案例通過,issue函數真的是多餘的。

屏幕截图 2017-03-06 14.31.44

***

回到原點

不知道鄉民們是否還記得當初為什麼會設計issue函數?因為Scenario告訴我們使用者會觸發「開三聯式發票(issue a company invoice)」這個動作。

屏幕截图 2017-03-06 14.34.32

 

到目前為止Teddy討論了兩種實作這個動作的可能:

  1. 另外建立InvoiceBuilder類別,由它來產生一個不可變(immutable)的Invoice物件。
  2. 把issue函數設計在Invoice類別身上,讓它具有開發票的能力。

討論至此推理的過程雖然有點囉嗦,但鄉民們應該可以看出來,無論是從需求面(問題領域)設計面(單一責任原則)來看,第1種方法感覺起來比較合適。不但反應了問題領域的行為,讓開出的發票無法修改也比較不會造成程式的問題。

***

軟體的可讀性、可修改性、可維護性,都是來自於這些點點滴滴的細節。對於初學者來說,很容易選擇第2種設計方式(用一個Invoice類別打通關)。雖然程式可以動,但付出的代價卻是降低可讀性、可修改性、可維護性。這個問題就算是開發團隊「宣稱」使用BDD/TDD,還是非常可能寫出「不乾淨」的程式。為什麼?因為對於初學者來說只考慮到「工法(BDD/TDD)」,卻沒留意這個工法的「初心」,也就是協助開發人員在不同的尺度上規範系統行為。沒有花腦筋與時間去探索系統行為,工法可以幫上忙的地方就相對有限。

***

友藏內心獨白:一不小心軟體很快就凝固變成硬體了。

沒有留言:

張貼留言