Test Pyramid理論基本大意是,單元測試是基礎,是我們應該花絕大多數時間去寫的部分,而集成測試等應該是冰山上面能看見的那一小部分。
那么對于這個case,正確的單元測試方法,應該是去驗證loadData()方法調用了DataModel的某個請求數據的方法,同時傳遞的參數是正確的。“調用了DataModel的方法,同時參數是。。。” 這個才是loadData()這個方法的“返回結果”。
Mock的概念以及Mockito框架
要驗證某個對象的某個方法得到調用了,就涉及到mock的使用。這里對mock的概念做個簡單介紹,以免很多同學不熟悉,mock就是創建一個虛假的、模擬的對象。在測試環境下,用來替換掉真實的對象。這樣就能達到兩個目的:1. 可以隨時指定mock對象的某個方法返回什么樣的值,或執行什么樣的動作。 2. 可以驗證mock對象的某個方法有沒有得到調用,或者是調用了多少次,參數是什么等等。
要使用mock,一般需要使用mock框架,目前安卓最常用的有兩個,Mockito和JMockit。兩者的區別是,前者不能mock static method和final class、final method,后者可以。我們依然采用的是Mockito,原因說起來慚愧,是因為剛開始并不知道JMockit這個東西,后來查了一些資料,看過很多對比Mockito和JMockit的文章,貌似大部分還是很看好JMockit的,只是有一個問題,那就是跟robolectric的結合也有一些bug,同時使用姿勢跟Mockito有較大的不同,因此一直沒有抽時間去實踐過。這個希望以后能夠做進一步的調查,到時候在給大家分享一下使用感受。
但是使用Mockito,就有一個問題,那就是static method和final class、final method沒有辦法mock,對于這點如何解決,我們稍后會介紹到。
在測試環境中使用mock:依賴注入
接下來的一個問題就是,如何在測試環境下,把DataModel換成mock的對象,而正式代碼中,DataModel又是正常的對象呢?
這個問題也有兩種解決方案,一是使用專門的testing product flavor;二是使用依賴注入。第一種方案就是用一個專門的product flavor來做testing,在這個testing flavor里面,里面把需要mock的類寫一份mock的implementation,然后通過factory提供給client,這個factory的接口在testing flavor和正式的flavor里面是一樣的,在跑testing的時候,專門使用這個testing flavor,這樣通過factory得到的就是mock的類。這種情況看起來很簡單,但其實很不靈活,因為只有一種mock實現;此外,代碼會變得很丑陋,因為你需要為每一個dependency提供一個factory,會覺得很刻意;再者,多了一個flavor,很多gradle任務都會變得很慢。關于這種方案,可以參考這個視頻。
因此,我們用的是第二種,依賴注入。先簡單介紹一下依賴注入這個模式,他的基本理念是,某一個類(比如說DataActivity),用到的內部對象(比如說DataModel)的創建過程不在DataActivity內部去new,而是由外部去創建好DataModel的實例,然后通過某種方式set給DataActivity。這種模式應用是非常廣泛的,尤其是在測試的時候。為了更方便的做依賴注入,如今有很多框架專門做這件事情,比如RoboGuice、Dagger、Dagger2等等。我們用的是Dagger2,理由很簡單,這是目前最好用的DI框架。
關于Dagger2的文章,之前我們群里也分享了不少,但是好像我并沒有看到講述沒有關于如何在測試環境下使用Dagger2的文章,這個還是略感遺憾的。離開單元測試,使用依賴注入就少了很有說服力的一個理由。
那么這里我就介紹一下,怎么樣把Dagger2應用到單元測試中。熟悉dagger2的童靴可能知道,Dagger2里面最關鍵的有兩個概念,Module 和Component。Module是負責生成諸如DataModel這樣被別人(比如DataActivity)使用的類的地方。用術語的話,被別人使用的類DataModel叫Dependency,使用到了別的類的類DataActivity叫Client。而Component則是供Client使用Dependency的統一接口。也就是說,DataActivity通過Component,來得到一份DataModel的實例。
現在,關鍵的地方來了,Component本身是不生產dependency的,它只是搬運工而已,真正生產dependency的地方在Module。所以,創建Component需要用到Module,不同的Module生產出不同的dependency。在正式代碼里面,我們使用正常的Module,生產正常的DataModel。而在測試環境中,我們寫一個TestingModule,讓它繼承正常的Module,然后override掉生產DataModel的方法,讓它生產mock的DataModel。在跑單元測試的時候,使用這個TestingModule來創建Component,這樣的話,DataActivity通過Component得到的DataModel對象就是mock出來的DataModel對象。
原文轉自:http://www.jianshu.com/p/9f7a992fe9ec