【單元測試】的四面向
【單元測試】改變了我程式設計的思維方式 這篇文章公開發表並貼到 Facebook 上的一些技術社團後,引發了不少討論。會有這麼熱烈的討論,我認為因為在各面向,大家對單元測試的了解程度情況不一,實行單元測試後產生不同的個人體驗結果,因此對單元測試有了不同的見解。所以就有人見識到單元測試的美好國度,也有人覺得沒有什麼,懷疑它到底有沒有效益? 就【單元測試】改變了我程式設計的思維方式 提到,單元測試只是一個程式開發的方法論之一,不管有沒有採用都可以開發出你的軟體程式。姑且不論有哪些效益,這網路上有太多討論了。我想從另一個角度,切入採用單元測試後,所產生的四種面向。對於種種的討論,正反辯證前,應該要先分辨是處於哪個面向裡,不同面向,討論著眼點應該會不一樣,不然討論容易失焦,牛頭不對馬嘴。 我觀察到的四面向有兩個變因 所以可以畫圖如下表示 先寫單元測試程式 | B | A 同一人撰寫--------+-------- 不同人撰寫 D | C | 先寫目標程式 我們就來逐項檢視,看看有甚麼不同。 【D 面向】都是由同一人撰寫,先寫目標程式,後寫單元測試大部分關於單元測試的討論,都是落於這個面向。也就是說我已經寫好了目標程式後,然後去"補寫"單元測試。這情境會發生的原因不外乎聽過單元測試種種好處的開發者,嘗試想寫寫看。或是為了想通過外包專案的檢核,規定要有單元測試報告,及涵蓋率門檻的開發者。 討論串理還有人提出一個看法,發現臭蟲時,會傾向要寫出一個測試的條件去証明那臭蟲已被抓到,這會花多一點時間,但是是值得的,可不想日後被人質問:「為何解決的 bug 又會再出現?」 除此之外,更積極的目的是要重構程式。針對要重構的目標程式碼,在動手之前先寫下相關的單元測試,先確定修改之前的程式行為為何? 然後執行重構後,再一次執行單元測試。PASS 就是沒有改壞,可以快速確認此單元測試的涵蓋範圍,重構後沒有引入新的 bug 。 這是正常直覺發生的面向。不過會有一個盲點要小心,不要落入陷阱,就是因為兩者都是同一開發人員撰寫,開發人員其實對於目標程式的實做細節應該是非常熟稔,所以在寫測試時,很有可能會鑽牛角尖的測試這些細節,變成為了測試而寫測試,盲目追高涵蓋率的情況。並不會去思考這樣設計是否正確合理。 【C 面向】某甲先寫目標程式,某乙後寫單元測試這情況常常發生於某乙需要維護某甲因為某些因素無法繼續維護而留下來的專案。不過其實會這樣實踐的人不多。很多情況就是在還沒完全搞懂商業邏輯的情況,需求又一直進來,只好硬著頭皮直接改目標程式。這樣作風險很高,建議依靠多寫些單元測試來打保險,修改完目標程式就可以馬上執行單元測試驗證是否正確。也就是執行 D 面向所提及的積極作為。 除此之外,還有一種情況就是單元測試可以用來"學習"第三方函式庫,把撰寫單元測試作為學習探索的工具,而不僅僅只是測試的工具。正如 Clean Code 第八章所描述。【單元測試】改變了我程式設計的思維方式 也有提及,可以參考一下。 【B 面向】都是由同一人撰寫,先寫單元測試,後寫目標程式這就是所謂的 TDD(Test-Driven Development),由 Kent Beck 所提出。 然而有一派人認為測試一詞很容易讓人誤解,以為是要測試什麼已經存在的程式,也只有已存在的程式才能夠被測試。不過 TDD 的作法是目標程式依據單元測試案例產生出來,是依照單元測試定義的規格產出,哪來需要什麼測試?於是就有 Dan North 就提出了 BDD(Behavior-Driven Development) 這一概念,說明撰寫的單元測試不是用來測試目標程式,而是透過單元測試的形式,制定目標程式的行為規格,依照此規格,花最小的力氣,寫出需要的目標程式。 所以強調的是實作前的規格制定,可以視為將規格文件以程式寫成,而單元測試只是這些規格的描述形式。 所以一個有趣的發現是有 BDD 宣稱的一個 Javascript 測試 framework Jasmine 沒有名為 test 的相關資料夾,但有名為 spec (意指specification) 資料夾,但是理面放的其實就是 behavior 形式的單元測試程式。 撰寫本文時,有幸拜讀同人的 不要把 TDD 和做測試混為一談 此篇文章,對於 TDD 的闡述已經非常的清楚明瞭,搭配業界實際的例子,解開大眾對 TDD 的誤解,精闢入理,值得細細品嘗詳讀一番。我摘要一些內容如下:
我實在想全文收錄,不過實在太多了,還是請大家點連結閱覽吧。 討論串裡也有討論到,不要成為 TDD/BDD 的基本教義派。我覺得這和"物件導向語言是否是程式設計的銀彈"是等價的問題,況且我們周遭很少有人推 TDD/BDD,不是嗎?有這樣的 issue 出現,通常就是這東西很熱門,開始流行的時刻。 另外,我也戲稱 TDD/BDD 為"許願式程式設計",【單元測試】改變了我程式設計的思維方式 亦有闡述。 【A 面向】某甲先寫單元測試,某乙才針對某甲的單元測試撰寫目標程式這面向非常的不直覺,我也罕有聽過有這樣的案例以及討論。我認為這面向是進階的 TDD/BDD ,某甲寫單元測試當作系統規格,然後交由某乙依據此規格,撰寫目標程式,實作出規格。 這很適合應用在外包專案的情況。某甲要外包某專案,所提出的規格可以不再是文件呈現,而是以單元測試的形式。這樣可以減少兩方的溝通落差,日後驗收也可以直接以這個標準來檢驗。不過前提是發包方某甲要對撰寫程式要有一定的程度概念,才寫得出像樣的單元測試型態的規格。不過發包方通常就是不懂程式才會外包,但又要要求對撰寫程式有概念,這很困難。不過我覺得這是很好的以單元測試來實踐專案分工的方式。 規模大的系統也比較有可能實行這面向,想想,系統如果大到開發人員需要專職的分工成 System Architect(SA)、System Designer(SD)、Programmer(PG)。SA 或 SD 就可以以單元測試為寫規格工具,撰寫單元測試,然後 PG 可以依照此規格撰寫出目標程式。可將單元測試作為設計和實作兩方共同的溝通工具,不用再花多餘的時間去撰寫,也許一直無法同步的規格文書;日後更可以作為驗收的依據,而且可用工具自動驗證。很美好,何樂而不為? 總結所以在討論實作"單元測試"適當與否之前,可以先釐清處於哪一個面向。而上各面向實行的難易度正如各面向所命名,A > B > C > D,不管分類為何,一致的看法就是一定要動手去作,才能了解體會單元測試的好處,或是一無是處。 討論串整理稍微整理條列一下,討論串裡大家的看法。 不去實作的理由: 要實做的理由: 注意事項: 覺得還是得有其他工具、方法才能相輔相成,產生威力,例如 詳細的討論可至這裡參考。 |