本節是單元測試的第三篇。我以為這是重中之重的一章。單元測試的關鍵在于代碼要可測。可測才能測。要做好單元測試,就必須在代碼的可測性方面努力,在設計、重構方面用心。本篇主要分享我在如何寫出可測性代碼方面的理解,與大家共勉!
單元測試(提升篇)
------編寫可測試性代碼
一、可測試性設計
1.
接口依賴
這是最重要的一點,因為這可以使得我們很容易的針對一個接口實現Mock對象,模擬/替換實際對象會變的很容易。達到使一個被測對象處于一個孤立環境的測試要求。
這里,ClassA依賴于ClassB的具體實現,TestCase根本無法獨立于ClassB對ClassA進行獨立測試。

因此,我們將ClassA改為依賴于接口B_Inf。這樣,可以很容易的實現一個Mock_B替換ClassB去ClassA進行孤立測試。
2.
依賴注入
一個方法對外部類的依賴,應該是可注入的。即可以通過構造方法、get/set方法的方式在外部將依賴關系注入。事實上,這也為我們在測試用例中替換待測類的依賴對象提供了機會。不應該出現在方法內部新建對象使用的情況。
3.
降低耦合度
待測類應與最少的類耦合,即最小交互原則。特別是要減少與那些離了具體環境就不能運行的類的耦合。可以通過門面模式等對外部調用進行隔離。
5.AOP
面向切面編程。給我們提供的啟示是,將真正需要測的邏輯分離出來。擺脫那些無意義且簡單重復的代碼對測試的干擾。
6. 明確的契約
方法一定要有明確清晰的輸入/輸出。建議在方法的注釋描述中,分三段“描述”“前置條件”“后置條件”。
二、可測試性重構
1.
可惡的靜態方法
在我們的代碼中有大量的調用靜態方法的地方。用起來我們很爽,但這對測試來說卻是災難。因為我們除了通過改變代碼創建stub來改變這些方法的行為外,我們沒有任何途徑。更要命的是,寫這些stub的代價非常的巨大,常常令人望而卻步。
解決方案:
將方法內部的這些調用提取成protected方法。在外部創建待測類的子類,重寫該protected方法。
最佳實踐:

這些靜態方法由單態類提供,單態類由工廠方法獲取,具體類使用這些單態類的接口。
我們在方法中通過接口使用對外部模塊的調用。一方面,隔離了外部模塊改變對我們產生的沖擊,另一方面,也使我們使用Mock替換實際的外部組件,創建孤立測試環境成為可能。
2.
待測類方法中,new出另一個對象并使用其方法
兩種方案:1)將new 出對象的過程封裝成protected方法。同重構1。2)將該對象提取成類屬性,即由使用關系變成關聯關系。
3.
分離不可測/不必測代碼
在不影響的情況下,將不可測部分分離到一些不需要測的簡單方法中去。或者將可測的部分提取到一個私有方法中去。然后針對這個私有方法進行測試。
通常這種做法使用范圍有限,但有些時候還是值的一試。
4.
單一職責
職責太多,肯定不好測。針對于這一點“不好測的方法必然不好用”。當方法過大,承擔責任過多時,拆分是應該的。
5.
為類提供一個空構造方法。
我們的各個測試方法都依賴于對象處于一個特定的狀態,滿足一定的前置條件。而要將對象置為我們希望的狀態,我們必須首先擁有一個對象。然而,很多時候,在一個隔離的單元測試環境下,構造函數由于各種原因不能正常初始化。
此時,可以為類提供一個的空的構造方法。在外部構造一個“裸”對象,而后根據前置條件將各個屬性設置成需要的Mock對象。
-----------------------------------------------------------------------------------------------------------------------------------------------------------
事實上,要想寫出具有可測性的代碼,最佳的辦法就是測試驅動開發。先寫測試代碼把功能體現出來,再寫功能代碼讓測試通過。這樣寫出的代碼顯而易見會更具有測試性。
posted on 2008-07-18 23:11
wukaichun 閱讀(1586)
評論(0) 編輯 收藏 所屬分類:
test