l

2017年2月28日 星期二

BDD(11)使用Scenario Outline指定多個範例(下)

Feb. 23 18:43~20:24

屏幕截图 2017-02-23 20.23.35

▲  圖片節錄自Google搜尋結果

 

Debug

開發人員A:看了錯誤訊息,發現這個case的稅金算出來是1元而不是0元,很顯然是稅金計算有問題

屏幕截图 2017-02-23 17.58.29_thumb[4]

屏幕截图 2017-02-23 18.00.09_thumb[1]

 

開發人員B:我記得稅金計算在InvoiceBuilder類別中的getVAT函數,它先呼叫getTaxExcludedPrice函數得到未稅價格,然後再把未稅價格乘上稅率得到稅金。

屏幕截图 2017-02-23 18.55.10

 

開發人員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。

屏幕截图 2017-02-23 19.21.25

 

開發人員B:因為還沒實作程式碼所以這個測試案例會失敗,現在讓我們把getTaxExcludedPrice從InvoiceBuilder類別移到InvoiceCalculator類別。

屏幕截图 2017-02-23 19.24.28

 

開發人員A:測試案例通過了,看起來bug應該是在getVAT函數身上。

屏幕截图 2017-02-23 19.24.54

 

開發人員B:我們趕快幫getVAT函數寫一個單元測試看看能不能重現這個bug。

屏幕截图 2017-02-23 19.28.39

 

開發人員A:現在來實作getVAT函數。

屏幕截图 2017-02-23 19.29.24

 

開發人員A:跑一下測試案例,果然失敗了。

屏幕截图 2017-02-23 19.32.12

 

開發人員B:奇怪,公式看起來沒錯啊,。

屏幕截图 2017-02-23 19.37.09

 

開發人員A:我們手動驗算看看,未稅價格10 乘上稅率 0.05 = 0.5,四捨五入之後變成1。

開發人員B:但是Product Owner告訴我們,10元以內的營業稅都是0,所以這算是一個特殊狀況。

開發人員A:難道我們要在公式前加上 if 條件句來排除這個狀況嗎?

開發人員B:啊,我想到了。既然計算未稅價格的getTaxExcludedPrice函式是對的,我們只要把getVAT函數改成這樣就可以了。

屏幕截图 2017-02-23 19.42.03

 

開發人員A:對耶,用含稅價格減去未稅價格不就好了,你好棒棒。跑一下單元測試案例確認一下。

屏幕截图 2017-02-23 19.43.04

 

開發人員B:單元測試案例通過了,現在可以回頭修改InvoiceBuilder類別,讓它的實作改呼叫InvoiceCalculator類別。

屏幕截图 2017-02-23 19.46.44

 

開發人員A:驗收測試也通過了,YA,可以跟Product Owner交差了。

屏幕截图 2017-02-23 19.47.37

***

結束這一回合

開發人員們:我們找到問題也改好了,你看原本的驗收測試都通過了,你看一下測試結果。

Product Owner:太好了,辛苦了。

Product Owner:關於開三聯發票還有其他問題嗎?

開發人員們:啊,還有一個問題。可以開0元發票嗎?

Product Owner:可以喔,只不過0元發票不能兌獎,不過能不能兌獎跟我們系統沒關係。我知道你要說什麼:「可以給我一個例子嗎?」例子在此,請服用。

含稅價格 稅金 未稅價格 備註
0 0 0 可以開零元發票

 

開發人員A:好,那我再加一個0元發票的例子。

屏幕截图 2017-02-23 20.03.34

 

開發人員:這9個例子都通過了。

屏幕截图 2017-02-23 20.04.06

 

Product Owner:好,這個scenario就先做到這樣,我們再找時間討論其他scenario。

***

討論

  • 從這個例子可以看出來由BDD銜接到TDD的部分,為了要支援10元免收營業稅的例子,我們用TDD的方式修改了程式設計,把InvoiceBuilder類別與計算發票相關金額有關函數套用Move Method重構移到新的InvoiceCalculator類別中。重構之後的設計也更加符合單一責任原則(Single Responsibility Principle)
  • 「當發現系統有bug,先寫一個失敗的測試案例來反應出這個bug,之後再修改程式直到測試案例通過」,這是敏捷開發中很常見的除錯過程。

***

友藏內心獨白:對於BDD/TDD應該慢慢有點感覺了吧。

沒有留言:

張貼留言