單元測試的目的不是尋找BUG;優秀的單元測試和糟糕的單元測試;關于編寫單元測試的一些建議
一、單元測試的目的不是尋找BUG
二、優秀的單元測試和糟糕的單元測試
三、關于編寫單元測試的一些建議
原文來自http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/
(Writing Great Unit Tests: Best and Worst Practices by:Steve Sanderson)
譯文:
三、關于編寫單元測試的一些建議
理論性的探讨已經足夠,是時候讨論一些實踐層面的問題了。下面是一些能讓你的單元測試處于之前所說的坐标軸的左邊的建議。
1. 確定每個測試方法與其它所有的測試方法的關系是正交的(相對獨立)
1.1 一個測試方法隻能用來測試一種行為,也不能把一種行為分散到多個測試方法中,否則如果日後行為發生改變,就需要修改多個地方。
1.2 不要做不必要的斷言(Assert),編寫測試方法前一定要先搞清楚驗證的行為是什麼
a)濫用斷言不會提高測試覆寫率
b)如果某個行為不屬于測試方法驗證的目标,就停止對其測試。關于這一點,TDD有個說法為:一個測試方法有且僅有一個斷言。
c)要時刻謹記:單元測試的目的是為了驗證方法的行為是否符合預期,而不是監視方法在各種情況的行為。
1.3 一次隻測試一個代碼單元
a) 你設計的軟體架構必須支援對每個單元的獨立測試(一個類或功能相關的幾個類),不然單元測試之間會有重疊,在這種情況下,某處測試代碼的微小改動可能造成數目龐大的級聯修改。如果你的軟體架構做不到支援單元測試,那麼軟體品質就無法得到保證,建議使用Inversion Of Control(控制反轉)的思想進行重構。
b) 排除所有對外部服務和狀态的依賴
引用外部服務會導緻測試重疊,而對外部狀态的依賴意味着單元測試在不同的情況會有不同的輸出。如果你編寫的單元測試必須按照一定的次序運作或者必須在資料庫和網絡就緒後才能運作,說明你已經走在了錯誤的道路上。(有時,單元測試代碼可能會修改靜态變量的值,盡量不要這樣做,如果無法避免,至少應在測試結束後将修改過的變量複原)。避免添加前置條件;還要避免在獨立的單元測試之前統一執行配置代碼(原文為Setup Code),否則我們無法确定每個單元測試依賴的假設是什麼,同時這也意味着你的單元測試思路出了問題。
c) 不要對配置代碼做單元測試。
我們先來明确配置代碼的定義。配置代碼不是每個代碼單元的公共部分。配置代碼是可以複制粘貼的,我個人将ASP.NET MVC 中的 filter也歸為配置代碼的範疇,像[Authorize]或[RequiresSs]之類的标簽則是摻雜在普通代碼中的配置代碼,對于這些代碼,最好使用內建測試的方法,從外部觀察它們的行為,使用單元測試是沒有意義的,這對你的設計沒有幫助,也無法幫您檢測缺陷。
2. 采用清晰統一的命名規範
如果你是在測試ProductController控制器中的Purchase方法(Action)在庫存為0時的行為,你可能會對應建立一個PurchasingTests類和一個名稱為ProductPurchaseAction_IfStockIsZero_RendersOutOfStockView()的單元測試方法,這種命名方法同時給出了主題(ProductController控制器中的Purchase方法)、情景(庫存為0)和結果(顯示“庫存為0”)。我不清楚這種命名方法是否已經有固定的名稱,但我知道很多人采用這種命名方法,我們是否可以考慮将其稱為”S/S/R”命名規範(Subject/Scenario/Result)...
盡量不要使用一些含混不清的命名,比如Purchase()或者OutOfStock(),這一類方法會很難維護,因為你根本無法搞清楚自己在維護什麼。
總之,單元測試可以提升項目品質,這一點是毫無疑問的。但對于許多人聲稱的“做(單元測試)總比不做(單元測試)好”,我是不同意的,單元測試可能産生極高的價值,卻也可能造成極大的負擔,這都取決于單元測試的編寫品質,取決于單元測試的編寫者能否很好地了解單元測試的原則和目标。

歡迎關注我的個人公衆号【菜鳥程式員成長記】