Sep. 15 15:30~17:42
在上禮拜(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算是在商業用途上有持續開發,其餘主流程式語言並沒有完整的支持。
好書一本,可惜在國內看的人很少。
讀過《Applying UML and Patterns》這本書的鄉民們,可能會記得書中提到,在完成Use Case之後,可以藉由撰寫「operation contract」(操作合約)來規範系統的行為。如果硬要類比到現在的BDD/TDD,operation contract就相當於BDD裡面的驗收測試案例,DBC就相當於TDD裡面的單元測試。
***
誤解三: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很好,但請趕快動手去試看看,這樣才有實質的幫助。
***
友藏內心獨白:誤把別人的力量當成自己的力量是一種很可怕的誤會。
補充一下,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"。