Feb. 23 18:43~20:24
▲ 圖片節錄自Google搜尋結果
Debug
開發人員A:看了錯誤訊息,發現這個case的稅金算出來是1元而不是0元,很顯然是稅金計算有問題。
開發人員B:我記得稅金計算在InvoiceBuilder類別中的getVAT函數,它先呼叫getTaxExcludedPrice函數得到未稅價格,然後再把未稅價格乘上稅率得到稅金。
開發人員A:哇,這個邏輯有點小複雜,是不是要寫個單元測試來確認一下?
開發人員B:對歐,怎麼之前我們沒測這兩個函數?
開發人員A:因為它們都是private函數,被issue函數給使用。我們是藉由測試issue函數間接測試這兩個private函數。
開發人員B:可是現在有bug,如果不對它們做個別的單元測試不太容易看出問題出在哪裡?
開發人員A:我們現在有兩個選擇,要嘛就是把這兩個函數先改成public,等測試完畢找到bug之後再改回private,或是使用一些特殊技巧讓JUnit可以測試private函數。
開發人員B:我覺得這兩個方法都不太好耶,尤其是第一個,先改成public等測試後沒問題再改回private那麼原本的單元測試案例就沒有作用了啊。
開發人員A:那怎麼辦?
***
Refactoring
開發人員B:我們分析一下,InvoiceBuilder的責任是產生一個Invoice,getVAT和getTaxExcludedPrice的責任是和計算發票相關金額有關。我們把這兩個函數變成private的原因是我們認為它們只是InvoiceBuilder的實作細節,一個InvoiceBuilder身上如果有發票金額計算公式的確有點怪。但是既然這些公式的邏輯需要釐清,代表「案情並不單純」,會不會有一種責任我們還沒找出來?
開發人員A:你的意思是說這兩個函數應該屬於另外一個類別?
開發人員B:嗯,我們試著新增一個InvoiceCalculator類別看看,然後套用Move Method重構把這兩個private函數移過去。
開發人員A:那我們先幫getTaxExcludedPrice函數寫一個單元測試,輸入含稅價格10和稅率0.05,未稅價格為10。
開發人員B:因為還沒實作程式碼所以這個測試案例會失敗,現在讓我們把getTaxExcludedPrice從InvoiceBuilder類別移到InvoiceCalculator類別。
開發人員A:測試案例通過了,看起來bug應該是在getVAT函數身上。
開發人員B:我們趕快幫getVAT函數寫一個單元測試看看能不能重現這個bug。
開發人員A:現在來實作getVAT函數。
開發人員A:跑一下測試案例,果然失敗了。
開發人員B:奇怪,公式看起來沒錯啊,。
開發人員A:我們手動驗算看看,未稅價格10 乘上稅率 0.05 = 0.5,四捨五入之後變成1。
開發人員B:但是Product Owner告訴我們,10元以內的營業稅都是0,所以這算是一個特殊狀況。
開發人員A:難道我們要在公式前加上 if 條件句來排除這個狀況嗎?
開發人員B:啊,我想到了。既然計算未稅價格的getTaxExcludedPrice函式是對的,我們只要把getVAT函數改成這樣就可以了。
開發人員A:對耶,用含稅價格減去未稅價格不就好了,你好棒棒。跑一下單元測試案例確認一下。
開發人員B:單元測試案例通過了,現在可以回頭修改InvoiceBuilder類別,讓它的實作改呼叫InvoiceCalculator類別。
開發人員A:驗收測試也通過了,YA,可以跟Product Owner交差了。
***
結束這一回合
開發人員們:我們找到問題也改好了,你看原本的驗收測試都通過了,你看一下測試結果。
Product Owner:太好了,辛苦了。
Product Owner:關於開三聯發票還有其他問題嗎?
開發人員們:啊,還有一個問題。可以開0元發票嗎?
Product Owner:可以喔,只不過0元發票不能兌獎,不過能不能兌獎跟我們系統沒關係。我知道你要說什麼:「可以給我一個例子嗎?」例子在此,請服用。
含稅價格 | 稅金 | 未稅價格 | 備註 |
0 | 0 | 0 | 可以開零元發票 |
開發人員A:好,那我再加一個0元發票的例子。
開發人員:這9個例子都通過了。
Product Owner:好,這個scenario就先做到這樣,我們再找時間討論其他scenario。
***
討論
- 從這個例子可以看出來由BDD銜接到TDD的部分,為了要支援10元免收營業稅的例子,我們用TDD的方式修改了程式設計,把InvoiceBuilder類別與計算發票相關金額有關函數套用Move Method重構移到新的InvoiceCalculator類別中。重構之後的設計也更加符合單一責任原則(Single Responsibility Principle)。
- 「當發現系統有bug,先寫一個失敗的測試案例來反應出這個bug,之後再修改程式直到測試案例通過」,這是敏捷開發中很常見的除錯過程。
***
友藏內心獨白:對於BDD/TDD應該慢慢有點感覺了吧。