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