天天看點

使用強大的 Mockito 測試架構來測試你的代碼

<b>本文講的是使用強大的 Mockito 測試架構來測試你的代碼,</b>

<b></b>

這篇教程介紹了如何使用 Mockito 架構來給軟體寫測試用例

如果需要往下學習,你需要先了解 Junit 架構中的單元測試。

單元測試的思路是在不涉及依賴關系的情況下測試代碼(隔離性),是以測試代碼與其他類或者系統的關系應該盡量被消除。一個可行的消除方法是替換掉依賴類(測試替換),也就是說我們可以使用替身來替換掉真正的依賴對象。

dummy object 做為參數傳遞給方法但是絕對不會被使用。譬如說,這種測試類内部的方法不會被調用,或者是用來填充某個方法的參數。

Fake 是真正接口或抽象類的實作體,但給對象内部實作很簡單。譬如說,它存在記憶體中而不是真正的資料庫中。(譯者注:Fake 實作了真正的邏輯,但它的存在隻是為了測試,而不适合于用在産品中。)

stub 類是依賴類的部分方法實作,而這些方法在你測試類和接口的時候會被用到,也就是說 stub 類在測試中會被執行個體化。stub類會回應任何外部測試的調用。stub 類有時候還會記錄調用的一些資訊。

mock object 是指類或者接口的模拟實作,你可以自定義這個對象中某個方法的輸出結果。

測試替代技術能夠在測試中模拟測試類以外對象。是以你可以驗證測試類是否響應正常。譬如說,你可以驗證在 Mock 對象的某一個方法是否被調用。這可以確定隔離了外部依賴的幹擾隻測試測試類。

我們選擇 Mock 對象的原因是因為 Mock 對象隻需要少量代碼的配置。

你可以手動建立一個 Mock 對象或者使用 Mock 架構來模拟這些類,Mock 架構允許你在運作時建立 Mock 對象并且定義它的行為。

一個典型的例子是把 Mock 對象模拟成資料的提供者。在正式的生産環境中它會被實作用來連接配接資料源。但是我們在測試的時候 Mock 對象将會模拟成資料提供者來確定我們的測試環境始終是相同的。

Mock 對象可以被提供來進行測試。是以,我們測試的類應該避免任何外部資料的強依賴。

通過 Mock 對象或者 Mock 架構,我們可以測試代碼中期望的行為。譬如說,驗證隻有某個存在 Mock 對象的方法是否被調用了。

Mockito 是一個流行 mock 架構,可以和JUnit結合起來使用。Mockito 允許你建立和配置 mock 對象。使用Mockito可以明顯的簡化對外部依賴的測試類的開發。

一般使用 Mockito 需要執行下面三步

模拟并替換測試代碼中外部依賴。

執行測試代碼

驗證測試代碼是否被正确的執行

使用強大的 Mockito 測試架構來測試你的代碼

如果你的項目使用 Gradle 建構,将下面代碼加入 Gradle 的建構檔案中為自己項目添加 Mockito 依賴

Eclipse IDE 支援 Gradle 和 Maven 兩種建構工具,是以在 Eclipse IDE 添加依賴取決你使用的是哪一個建構工具。

在 Eclipse RCP 應用依賴通常可以在 p2 update 上得到。Orbit 是一個很好的第三方倉庫,我們可以在裡面尋找能在 Eclipse 上使用的應用和插件。

使用強大的 Mockito 測試架構來測試你的代碼

如果在代碼中靜态引用了<code>org.mockito.Mockito.*;</code>,那你你就可以直接調用靜态方法和靜态變量而不用建立對象,譬如直接調用 mock() 方法。

除了上面所說的使用 mock() 靜态方法外,Mockito 還支援通過 <code>@Mock</code> 注解的方式來建立 mock 對象。

如果你使用注解,那麼必須要執行個體化 mock 對象。Mockito 在遇到使用注解的字段的時候,會調用<code>MockitoAnnotations.initMocks(this)</code> 來初始化該 mock 對象。另外也可以通過使用<code>@RunWith(MockitoJUnitRunner.class)</code>來達到相同的效果。

通過下面的例子我們可以了解到使用<code>@Mock</code> 的方法和<code>MockitoRule</code>規則。

告訴 Mockito 模拟 databaseMock 執行個體

Mockito 通過 @mock 注解建立 mock 對象

使用已經建立的mock初始化這個類

在測試環境下,執行測試類中的代碼

使用斷言確定調用的方法傳回值為 true

驗證 query 方法是否被 <code>MyDatabase</code> 的 mock 對象調用

當我們需要配置某個方法的傳回值的時候,Mockito 提供了鍊式的 API 供我們友善的調用

<code>when(…​.).thenReturn(…​.)</code>可以被用來定義當條件滿足時函數的傳回值,如果你需要定義多個傳回值,可以多次定義。當你多次調用函數的時候,Mockito 會根據你定義的先後順序來傳回傳回值。Mocks 還可以根據傳入參數的不同來定義不同的傳回值。譬如說你的函數可以将<code>anyString</code> 或者 <code>anyInt</code>作為輸入參數,然後定義其特定的放回值。

對于無傳回值的函數,我們可以使用<code>doReturn(…​).when(…​).methodCall</code>來獲得類似的效果。例如我們想在調用某些無傳回值函數的時候抛出異常,那麼可以使用<code>doThrow</code> 方法。如下面代碼片段所示

Mockito 會跟蹤 mock 對象裡面所有的方法和變量。是以我們可以用來驗證函數在傳入特定參數的時候是否被調用。這種方式的測試稱行為測試,行為測試并不會檢查函數的傳回值,而是檢查在傳入正确參數時候函數是否被調用。

@Spy或者<code>spy()</code>方法可以被用來封裝 java 對象。被封裝後,除非特殊聲明(打樁 stub),否則都會真正的調用對象裡面的每一個方法

方法<code>verifyNoMoreInteractions()</code>允許你檢查沒有其他的方法被調用了。

我們也可以使用<code>@InjectMocks</code> 注解來建立對象,它會根據類型來注入對象裡面的成員方法和變量。假定我們有 ArticleManager 類

這個類會被 Mockito 構造,而類的成員方法和變量都會被 mock 對象所代替,正如下面的代碼片段所示:

建立ArticleManager執行個體并注入Mock對象

<code>ArgumentCaptor</code>類允許我們在verification期間通路方法的參數。得到方法的參數後我們可以使用它進行測試。

Mockito當然也有一定的限制。而下面三種資料類型則不能夠被測試

final classes

anonymous classes

primitive types

在 Android 中的 Gradle 建構檔案中加入 Mockito 依賴後就可以直接使用 Mockito 了。若想使用 Android Instrumented tests 的話,還需要添加 dexmaker 和 dexmaker-mockito 依賴到 Gradle 的建構檔案中。(需要 Mockito 1.9.5版本以上)

建立一個包名為<code>com.vogella.android.testing.mockito.contextmock</code>的Android應用,添加一個靜态方法 ,方法裡面建立一個包含參數的Intent,如下代碼所示:

使用 Mockito 建立一個單元測試來驗證在傳遞正确 extra data 的情況下,intent 是否被觸發。

是以我們需要使用 Mockito 來 mock 一個<code>Context</code>對象,如下代碼所示:

建立一個 Api,它可以被 Mockito 來模拟并做一些工作

實作 <code>TwitterClient</code>類,它内部使用到了 <code>ITweet</code> 的實作。但是<code>ITweet</code>執行個體很難得到,譬如說他需要啟動一個很複雜的服務來得到。

為了能夠不啟動複雜的服務來得到 <code>ITweet</code>,我們可以使用 Mockito 來模拟得到該執行個體。

現在 <code>TwitterClient</code> 可以使用 <code>ITweet</code> 接口的實作,當調用 <code>getMessage()</code> 方法的時候将會列印 "Using Mockito is great" 資訊。

確定 getMessage() 方法至少調用一次。

運作測試,檢視代碼是否測試通過。

因為 Mockito 不能夠 mock 靜态方法,是以我們可以使用 <code>Powermock</code>。

我們模拟了 NetworkReader 的依賴,如下代碼所示:

有時候我們可以在靜态方法周圍包含非靜态的方法來達到和 Powermock 同樣的效果。

<b>原文釋出時間為:2016年07月20日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>

繼續閱讀