本節(jié)是單元測試的第三篇。我以為這是重中之重的一章。單元測試的關(guān)鍵在于代碼要可測。可測才能測。要做好單元測試,就必須在代碼的可測性方面努力,在設(shè)計、重構(gòu)方面用心。本篇主要分享我在如何寫出可測性代碼方面的理解,與大家共勉!
單元測試(提升篇)
------編寫可測試性代碼
一、可測試性設(shè)計
1. 接口依賴
這是最重要的一點,因為這可以使得我們很容易的針對一個接口實現(xiàn)Mock對象,模擬/替換實際對象會變的很容易。達到使一個被測對象處于一個孤立環(huán)境的測試要求。
這里,ClassA依賴于ClassB的具體實現(xiàn),TestCase根本無法獨立于ClassB對ClassA進行獨立測試。
因此,我們將ClassA改為依賴于接口B_Inf。這樣,可以很容易的實現(xiàn)一個Mock_B替換ClassB去ClassA進行孤立測試。
2. 依賴注入
一個方法對外部類的依賴,應(yīng)該是可注入的。即可以通過構(gòu)造方法、get/set方法的方式在外部將依賴關(guān)系注入。事實上,這也為我們在測試用例中替換待測類的依賴對象提供了機會。不應(yīng)該出現(xiàn)在方法內(nèi)部新建對象使用的情況。
3. 降低耦合度
待測類應(yīng)與最少的類耦合,即最小交互原則。特別是要減少與那些離了具體環(huán)境就不能運行的類的耦合。可以通過門面模式等對外部調(diào)用進行隔離。
5.AOP
面向切面編程。給我們提供的啟示是,將真正需要測的邏輯分離出來。擺脫那些無意義且簡單重復(fù)的代碼對測試的干擾。
6. 明確的契約
方法一定要有明確清晰的輸入/輸出。建議在方法的注釋描述中,分三段“描述”“前置條件”“后置條件”。
二、可測試性重構(gòu)
1. 可惡的靜態(tài)方法
在我們的代碼中有大量的調(diào)用靜態(tài)方法的地方。用起來我們很爽,但這對測試來說卻是災(zāi)難。因為我們除了通過改變代碼創(chuàng)建stub來改變這些方法的行為外,我們沒有任何途徑。更要命的是,寫這些stub的代價非常的巨大,常常令人望而卻步。
解決方案:
將方法內(nèi)部的這些調(diào)用提取成protected方法。在外部創(chuàng)建待測類的子類,重寫該protected方法。
最佳實踐:
這些靜態(tài)方法由單態(tài)類提供,單態(tài)類由工廠方法獲取,具體類使用這些單態(tài)類的接口。
我們在方法中通過接口使用對外部模塊的調(diào)用。一方面,隔離了外部模塊改變對我們產(chǎn)生的沖擊,另一方面,也使我們使用Mock替換實際的外部組件,創(chuàng)建孤立測試環(huán)境成為可能。
2. 待測類方法中,new出另一個對象并使用其方法
兩種方案:1)將new 出對象的過程封裝成protected方法。同重構(gòu)1。2)將該對象提取成類屬性,即由使用關(guān)系變成關(guān)聯(lián)關(guān)系。
3. 分離不可測/不必測代碼
在不影響的情況下,將不可測部分分離到一些不需要測的簡單方法中去。或者將可測的部分提取到一個私有方法中去。然后針對這個私有方法進行測試。
通常這種做法使用范圍有限,但有些時候還是值的一試。
4. 單一職責(zé)
職責(zé)太多,肯定不好測。針對于這一點“不好測的方法必然不好用”。當(dāng)方法過大,承擔(dān)責(zé)任過多時,拆分是應(yīng)該的。
5. 為類提供一個空構(gòu)造方法。
我們的各個測試方法都依賴于對象處于一個特定的狀態(tài),滿足一定的前置條件。而要將對象置為我們希望的狀態(tài),我們必須首先擁有一個對象。然而,很多時候,在一個隔離的單元測試環(huán)境下,構(gòu)造函數(shù)由于各種原因不能正常初始化。
此時,可以為類提供一個的空的構(gòu)造方法。在外部構(gòu)造一個“裸”對象,而后根據(jù)前置條件將各個屬性設(shè)置成需要的Mock對象。
-----------------------------------------------------------------------------------------------------------------------------------------------------------
事實上,要想寫出具有可測性的代碼,最佳的辦法就是測試驅(qū)動開發(fā)。先寫測試代碼把功能體現(xiàn)出來,再寫功能代碼讓測試通過。這樣寫出的代碼顯而易見會更具有測試性。
原地址:http://www.aygfsteel.com/wukaichun600/archive/2008/07/18/214125.html