l

2017年3月1日 星期三

BDD(12) 第二個開發票Scenario:從BDD到TDD

Feb. 23 22:20~24:00;Feb. 24 00:00~12:18

屏幕截图 2017-02-24 00.17.03

 

新劇情

Product Owner:用含稅價格來開發票的劇情做好了,接下來我們要考慮用未稅價格開發票的例子。

開發人員們:用未稅價格開發票?

Product Owner:對,有些商品為了要看起來比較便宜,會標示未稅價格,所以我們的開發票程式除了接受含稅價格開發票以外,還要能夠接受未稅價格開發票,然後算出稅金與含稅價格。

開發人員們:你的說明很清楚了,可以給我們一個例子嗎?

Product Owner:除了下面被我劃掉的這個case不算以外,其他例子和上一個劇情一樣。

含稅價格 稅金 未稅價格 備註
36000 1714 34286 Refactoring定價
17900 852 17048 Scrum早鳥
17000 810 16190 Scrum泰迪之友
21000 1000 20000 Scrum定價
99 5 94 四捨五入案例
1 0 1 邊界條件
10 0 10 邊界條件
11 1 10 邊界條件
0 0 0 可以開零元發票

 

開發人員們:好,那我們用Cucumber新增一個Scenario Outline。下面這樣子對嗎?

屏幕截图 2017-02-23 22.32.19

 

Product Owner:沒錯,資料是一樣的只不過這個劇情中用未稅價格來開發票。

***

從BDD到TDD

開發人員A:讓我們跑一下驗收測試,有2個step definition還沒定義。

屏幕截图 2017-02-23 22.33.36

 

開發人員B:2個step definition還沒定義?這個劇情和上一個一樣不是有5 個step嗎,為什麼Cucumber說只有2個step definition沒有定義?

開發人員A:喔,因為1、3、4條step內容和上一個劇情一模一樣,所以會找到之前已經寫好的step definition。

屏幕截图 2017-02-23 22.44.03

 

開發人員B:喔,原來不同Scenario或Scenario Outline裡面的step如果內容一樣,會對應到相同的step definition喔。

開發人員A:對啊,因為Cucumber是靠regular expression從step去找step definition,所以相同的step自然會對應到同一個step definition。

開發人員B:了解。那我們來實作這兩個step definition吧。

開發人員A:我們先實作「And the tax excluded price provided is <tax_excluded>」這個step definition。

屏幕截图 2017-02-23 22.49.26

 

開發人員B:所以我們要幫InvoiceBuilder類別增加withTaxExcludedPrice函數。

屏幕截图 2017-02-23 22.51.38

 

開發人員A:這個簡單。

屏幕截图 2017-02-23 22.54.40

 

開發人員B:跑一下測試案例,前三個步驟都通過了,但是稅金算錯,應該是1714結果是0。

屏幕截图 2017-02-23 22.55.39

屏幕截图 2017-02-23 22.58.15

 

開發人員A:我們來看看是什麼問題。開發票之後的稅金算錯,看看InvoiceBuilder類別的開發票issue函數。

屏幕截图 2017-02-23 23.04.58

 

開發人員B:啊,我知道了。我們之前都假設由含稅價格(taxIncludedPrice)來計算出未稅價格與稅金,但這個劇情中我們是用未稅價格來計算,沒有設定taxIncludedPrice屬性,所以算出來的稅金變成0。

開發人員A:那我們要怎麼用測試案例來記錄這件是呢?

開發人員B:先寫個單元測試好了。

屏幕截图 2017-02-23 23.13.29

 

開發人員A:執行失敗,果然含稅價格算出來是0,因為根本沒有計算到。

屏幕截图 2017-02-23 23.15.04

 

開發人員B:所以在開發票前要先檢查含稅價格是否為0,如果是就先用未稅價格算出含稅價格,再帶入原本的公式傳回新的發票就可以了。

屏幕截图 2017-02-23 23.24.46

 

開發人員A:那我們要在InvoiceCalculator類別中新增getTaxIncludePrice函數,輸入未稅價格和稅率,傳回含稅價格。先寫一個單元測試來表達這件事。

屏幕截图 2017-02-23 23.28.47

 

開發人員B:可以實作getTaxIncludePrice函數,含稅價格等於未稅價格乘上(1+稅率)。

屏幕截图 2017-02-23 23.28.13

 

開發人員A:耶,InvoiceCalculator類別的單元測試都通過了。

屏幕截图 2017-02-23 23.30.10

 

開發人員A:現在可以回頭跑剛剛失敗InvoiceBuilder_could_issue_an_invoice_with_tax_excluded_price_and_vat_rate測試案例了。

屏幕截图 2017-02-23 23.32.34

 

開發人員A:執行結果成功。

屏幕截图 2017-02-23 23.32.50

***

用更多單元測試揭露物件行為

開發人員A:單元測試都成功了,我們可以回到剛剛失敗的驗收測試。

開發人員B:等一下,我剛剛想到,如果有人含稅價格和未稅價格都沒設定就開發票會怎樣?

開發人員A:好問題,我們寫個單元測試案例看看會發生什麼事。

屏幕截图 2017-02-23 23.45.06

 

開發人員A:執行結果通過,就等於開了張0元發票。Product Owner給我們的例子中開0元發票是OK的,所以這個行為沒錯。

屏幕截图 2017-02-23 23.49.13

 

開發人員B:耶,那我們寫一個單元測試來看看如果什麼資料都沒提供就直接開發票看看會怎樣。

屏幕截图 2017-02-23 23.53.37

 

開發人員A:我要寫一個慘字,出現NullPointerException。

屏幕截图 2017-02-23 23.54.45

 

開發人員B:第35行出錯,看看第35行是做什麼的。呼叫InvoiceCalculator.getTaxIncludedPrice函數計算含稅價格,傳入未稅價格和代表稅率的Percentage屬性vatRate轉成double傳給這個函數。啊,我知道了,因為沒有設定稅率所以vatRate屬性是null,才會出現NullPointerException。

屏幕截图 2017-02-23 23.55.41

 

開發人員A:我們在InvoiceBuilder類別的建構函數中給稅率百分比物件一個0初始值好了。

屏幕截图 2017-02-24 00.02.49

 

開發人員A:測試案例通過了。

屏幕截图 2017-02-24 00.07.08

 

開發人員A:你還有想到有哪些物件行為需要確認嗎?

開發人員B:暫時沒有了。

***

從TDD回到BDD

開發人員A:經過一番奮鬥,單元測試都已經通過了,我們現在回頭執行早先失敗的驗收測試案例,看看現在是否可通過。

開發人員B:太好了,兩個劇情都通過了。

屏幕截图 2017-02-23 23.36.22

 

開發人員們:Product Owner大大,第二個劇情也實作完成,請看測試案例。

Product Owner:太好了,下次可以進行新的劇情了。

***

友藏內心獨白:真的是行為驅動著開發活動。

沒有留言:

張貼留言