不少公司不少人在實踐單元測試中總覺的很困難的一個很重要的緣由就是其代碼自己不具備可測試性。他們每每會走入一個誤區。面對一個幾千行、邏輯混亂的方法而抓耳撓腮的想着用十八般武藝,各類框架去寫這個方法的單元測試,而最終不得不以失敗而了結,耗費了大量的時間卻徒勞而無功。實際上是他們忽略了這個方法的自己是不具備可測試性的。框架
所以咱們在要對一個方法進行單測以前,必定要先看一下它是否是具備可測試性,若是不具備,那麼咱們應該先對其進行重構以提升其可測試性。
在可測試的設計中,你應該很容易爲代碼的每一段邏輯(循環、i語句和 switchi等)快速編寫一個單元測試,這些單元測試具備以下屬性函數
若是編寫這樣的測試很困難,或者須要很長時間,那這個系統就不是一個可測試的,若是把測試看作系統的一個用戶,可測試性設計就成爲一種思惟方式。單元測試
若是你要測試的方法真的有幾百行上千行,那麼我建議先用重構一書中的方法去解決它。其次關於可測試性在實踐過程當中是有一些技巧的。
2.1 方法可重寫
在Java中,方法默認就是虛擬方法,可重寫的。而在NET中,若是想要替換一個方法的行爲,你須要明確地把方法設置爲虛擬方法,才能進行重寫。所以咱們能夠在設計之初就儘可能把方法用關鍵字virtual標記。測試
若是咱們的代碼都可以保持面向接口設計的原則,意味着咱們的依賴都是很容易被替換的,可讓咱們在測試中年很容易取創造真實對象的僞對象。編碼
密封類就是將一個類封閉起來,斷其子孫的一種方式。
密封方法則不是爲了防止繼承而是防止重寫,並且它是爲了重寫基類的虛方法並提供具體的實現,同時防止其後繼類(派生類)再次重寫該虛方法
不管是咱們的被測方法仍是其依賴的方法屬於一個密封類,那麼意味着這個方法是不能被重寫的,在測試中也就不能去替換它。所以應該儘可能避免使用密封類設計
不少人在寫代碼的時候習慣於在一個方法內部初始化對象,以下面這樣code
public void GetName(int userId){ //方法內部初始化類 return new User(userId).GetName(); }
這樣的設計實際上是違背了代碼的低耦合原則的,也不具備可測試性,所以咱們在開發中應該儘可能避免在方法中初始化另外一個對象。對於外部依賴的對象可使用依賴注入的方式。對象
要在測試中替換一個靜態方法的行爲,是很是困難的。
要處理這種狀況,咱們可使用抽取和重寫的方法進行重構,把這個靜態方法抽象出去。
一個更爲極端的作法是:避免使用任何的靜態方法。這樣的話,每一段邏輯都是一個類實例的一部分,使得這段邏輯更容易替換。有些進行單元測試或者測試驅動開發的人不喜歡使用單例,緣由之一就是單例缺乏可替換性。單例是靜態的公共共享資源,很難重寫它們。
要徹底避免使用靜態方法可能會過於困難,可是你能夠嘗試在應用程序中儘可能少使用單例或者靜態方法,這樣在測試時會變得容易一些。繼承
不管是構造函數仍是靜態構造函數內的邏輯一樣是咱們沒法在測試中重寫控制它的,所以咱們須要避免在構造函數和靜態構造函數中包含邏輯代碼。接口
關於可測試性設計雖然是有着許多技巧,但若是你掌握了代碼編寫的內容心法"SOLID原則",而且熟練應用於編碼中,其實你的代碼絕大多數都是具備良好的可測試性的。
可測試性當然能夠更加方便的讓咱們對代碼進行單元測試,但有些時候也會給咱們帶來一些「麻煩」。所以它也是一個很有爭議的話題。有些人認爲可測試性是好的設計應該具備的特徵之一。也有些認爲可測試性會破壞原有的設計帶來一些反作用。
那麼可測試性設計會帶來哪些「麻煩」呢
大多數狀況下,設計時以可測試性爲目標會增長工做量,由於比起不考慮可測試性的設計比起可測試性設計須要編寫更多的代碼。
可測試性設計有時會讓人以爲,它把簡單的事情過於複雜化了。有些接口的使用讓你感受很彆扭或者設計公開了你不曾考慮過的類行爲語義。並且,當使用了不少接口把東西進行抽象以後,若是你要瀏覽基礎代碼找到一個方法的真正實現代碼,會更加困難和麻煩。
從一些可測試性的技巧咱們能夠知道有些時候爲了讓代碼更具備可測試性,會破壞一些原有的設計原則。
正如人月神話一書中所說,在軟件開發這一行業中沒有銀彈。一門技術或者方法論在給咱們提供了一些幫助的同時也會帶來一些問題。這就須要咱們在具體使用的過程當中****case by case,根據可測試性設計所帶來的優缺點找到一個平衡點!