l

2014年9月18日 星期四

關於BDD/TDD的三大誤解

Sep. 15 15:30~17:42

image

 

在上禮拜(9月12、13)「單元測試與持續整合實作班」課程中,有兩位學員提到他們希望在課程中可以學會TDD…

Teddy:TDD是一種開發方法,不是測試。我們這門課主要是講單元測試與持續整合,我會介紹BDD/TDD的概念,但不是要教會大家做BDD/TDD。BDD/TDD是另外一門課程。

學員:可是TDD可以增進軟體設計品質,並且減少bug,又可以(以下省略10種優點)….

Teddy:你把TDD講的太神了吧,好像咒語一樣,只要念一聲咒語就可以化解一切苦厄。這種想法好像早年的物件導向設計一樣,學會物件導向設計「可以幫助」你做出彈性、易修改的系統,但這不代表只要是物件導向設計的系統就一定是有彈性、易修改,這是兩回事。你還是可以用物件導向技術做出很僵化又沒彈性的系統出來。同樣地,BDD/TDD「可以幫助」你設計出可測性較高、耦合性較低的軟體,但不代表只要是「宣稱採用BDD/TDD」,就自動具備這些優點。

***

誤解一:TDD是一種測試方法

這個錯誤觀念已經被不同的人提過N次了,但是還是有人認為TDD是一種「測試方法」,只要用了TDD就可以自動減少系統的bug。錯,TDD是一種「開發方法」,藉由先定義規格,再撰寫程式的方式來開發軟體。

鄉民:應該不是這樣子吧!軟體工程的書早就說過,要先有規格再寫程式,這樣子TDD有什麼特別的?

Teddy:就是這樣子,TDD只是回歸到原本軟體工程告訴我們要做的事—先定義規格,再開發軟體(寫production code)。和傳統的方式不同之處在於,TDD的規格本身就是可執行文件(請參考〈BDD(1):詳盡的文件就是可用的軟體〉),將規格以可執行的「測試案例」來表達。這樣的好處是,因為驗收標準(規格)已經先以可自動執行的測試案例形式寫好,一旦production code完成之後,測試案例(也就是驗收規格)已經存在,可以避免先寫production code,然後以沒時間為藉口,不寫test code的情況。另外,因為規格文件可以被執行,所以可以自動驗證規格與實作在多次修改之後是否依舊保持一致。也因為如此,所以和傳統開方法方式相比,會有比較低的bug比率。最後,因為每次實作production code都只專注在讓先前用測試案例所定義的規格通過即可,因此可以降低過度設計(over design)的情況。

***

誤解二:TDD很神

TDD是Kent Beck所「發明」的,Kent Beck是大師這一點應該沒有爭議,但這不代表TDD是Kent Beck無中生有所創造出來的。Poppendieck說:「敏捷開發基礎的關鍵技術在軟體工程這個名詞出現之後(1968)的五年內都已經出現了前身」。還提出TDD算是Dijkstra對於程式開發看法—「用證明的方式來確定程式的正確性」的一種實踐。TDD先用測試案例來制定規格,接著才寫滿足規格的程式,也就是用測試來取代數學證明,但是概念上是一樣的(先有規格與驗收標準,再依此實作)。

早在TDD之前,已經有人嘗試以比較實務的做法來實踐Dijkstra的理論,最著名的應該就是Design By Contract(DBC)。DBC概念提出之後廣受軟體開發者的好評(至少在歐美挑眉質疑),可惜因為這是一種設計思維的「典範轉移」,落實到軟體開發上面,需要IDE、程式語言與函式庫的支援(等於整個開發生態系都要支援),最後只有提出DBC的Meyer所設計的Eiffel語言與Eiffel Studio算是在商業用途上有持續開發,其餘主流程式語言並沒有完整的支持。

螢幕截圖 2014-09-15 17.33.19

好書一本,可惜在國內看的人很少。

 

讀過《Applying UML and Patterns》這本書的鄉民們,可能會記得書中提到,在完成Use Case之後,可以藉由撰寫「operation contract」(操作合約)來規範系統的行為。如果硬要類比到現在的BDD/TDD,operation contract就相當於BDD裡面的驗收測試案例,DBC就相當於TDD裡面的單元測試。

螢幕截圖 2014-09-15 17.34.15

 

***

誤解三:TDD可治百病

有一位朋友很興奮地跟Teddy敘述TDD的好處。

Teddy:所以你已經在你參與的專案中應用了TDD,而且也獲得了這些好處?

朋友:嗯…還沒有,但我想只要開始導入BDD/TDD,很多現在遇到的問題便可迎刃而解。

Teddy:這樣啊,那你知道要怎麼開始導入BDD/TDD嗎?

朋友:就是讓業務單位的人跟工程師一起合作,先用Given..When..Then的格式把需求的規格訂出來,接下來便可由驗收測試導出TDD的單元測試,然後就可以產生高品質、易測性且低偶合的production code。

Teddy:你覺的業務單位的人和工程師有能力寫出合適的驗收測試嗎?

朋友:為什麼不行?不就是套進Given..When..Then的格式就好了,很簡單啊。

Teddy:寫user story也是只要套入In order to….As a [Role]…I can…這種格式,但是你知道有多少story徒具形式而無實際價值嗎?

朋友:我覺得你想太多了,總之BDD/TDD跟少林功夫一樣,就是棒。

TDD身為一種開發方法,最容易理解的就是它的開發流程。先寫一個失敗的測試案例,然後以最簡單的方式實作production code讓測試案例通過,待功能完成之後,重構程式(red、green、refactor)。但流程是空的,難的部分在於如何用測試案例定義規格、如何讓眾多的可執行規格可以持續用來驗證系統正確性,以及實作完成之後的重構工作。就好像套用DBC方法,難的不在於pre-condition、post-condition、class invariant這些觀念,而是在於如何幫每個類別的函數定義出pre-condition與post-condition。實作的能力(定義規格、執行規格、重構等),不會因為你採用TDD而自動擁有,還是需要持續學習

***

寫這一篇不是要反對或唱衰BDD/TDD/ATDD/Specification by Example這些方法,實際上Teddy還蠻認同這種做法,尤其是executable specification(可執行的規格)這一點。主要是想提醒一下,最近遇到不少剛接觸TDD的朋友,嘴上不斷地過度宣揚它的好處,一不小心就順便慛眠自己,誤以為自己已經具備這樣的經驗與能力。這種「症狀」對專案不但沒有幫助,反而會讓自己停滯不前。相信BDD/TDD很好,但請趕快動手去試看看,這樣才有實質的幫助。

***

友藏內心獨白:誤把別人的力量當成自己的力量是一種很可怕的誤會

1 則留言:

  1. 補充一下,Kent Beck 在 Quora 上分享過他如何 "再發現" TDD

    http://www.quora.com/Test-Driven-Development/Why-does-Kent-Beck-refer-to-the-rediscovery-of-test-driven-development

    可惜,他到現在還是不記得是那一本 "Ancient Book"。

    回覆刪除