Mockito入门
本文是我們名為“ 用Mockito進行測試 ”的學院課程的一部分。
在本課程中,您將深入了解Mockito的魔力。 您將了解有關“模擬”,“間諜”和“部分模擬”的信息,以及它們相應的Stubbing行為。 您還將看到使用測試雙打和對象匹配器進行驗證的過程。 最后,討論了使用Mockito的測試驅動開發(fā)(TDD),以了解該庫如何適合TDD的概念。 在這里查看 !
目錄
1.為什么要模擬? 2. Mockito框架簡介在本教程中,我們將研究Mockito Mocking Framework,并通過將其添加到類路徑中來準備一個Eclipse項目來使用它。
1.為什么要模擬?
我們編寫的所有代碼都有一個相互依賴的網絡,它可以調用其他幾個類的方法,而這些類又可以調用其他方法。 確實,這就是面向對象編程的意圖和力量。 通常在編寫功能代碼的同時,我們還將以自動化單元測試的形式編寫測試代碼。 我們使用這些單元測試來驗證代碼的行為,以確保其行為符合我們的預期。
當我們對代碼進行單元測試時,我們希望對其進行隔離測試,并且希望對其進行快速測試。 為了進行單元測試,我們只關心在當前測試的類中驗證我們自己的代碼。 通常,我們還希望非常定期地執(zhí)行單元測試,重構時以及在持續(xù)集成環(huán)境中工作時,每小時可能要執(zhí)行多次。
這是我們所有相互依存成為問題的時候。 我們可能最終在另一個類中執(zhí)行代碼,該類具有導致單元測試失敗的錯誤。 想象一下我們用來從數(shù)據庫讀取用戶詳細信息的類,如果要運行單元測試時沒有數(shù)據庫,會發(fā)生什么? 想象一下,一個調用多個遠程Web服務的類,如果它們出現(xiàn)故障或需要很長時間響應怎么辦? 我們的單元測試可能會由于我們的依賴關系而失敗,而不是因為我們的代碼行為出現(xiàn)問題。 這是不希望的。
除此之外,可能很難強制我們想要確保代碼正確處理的特定事件或錯誤條件。 如果我們要測試某個反序列化對象的類會正確處理可能的ObjectStreamException怎么辦? 如果我們要測試合作者的所有邊界返回值怎么辦? 確保將某些計算值正確傳遞給協(xié)作者該怎么辦? 如果可能的話,復制我們的測試條件可能需要花費大量的代碼并花費很長時間。
如果使用模擬,所有這些問題都會消失。 嘲笑就像是我們與之合作的類的替代品一樣,它們取代了他們的位置,并按照我們告訴他們的表現(xiàn)去精確地表現(xiàn)。 嘲弄讓我們假裝我們真正的合作者在那兒,即使他們不在。 更重要的是,可以對模擬程序進行編程以返回我們想要的任何值,并確認將任何值傳遞給它們。 模擬程序立即執(zhí)行,不需要任何外部資源。 假人會返回我們告訴他們的東西,拋出我們想讓他們拋出的任何異常,并將按需一遍又一遍地執(zhí)行這些操作。 他們讓我們僅測試我們自己代碼的行為,以確保我們的類能夠正常工作,而不管其協(xié)作者的行為如何。
有幾種可用于Java的模擬框架,每個框架都有自己的語法,自己的長處和缺點。 在本教程中,我們將使用Mockito框架,它是最流行的可用模擬框架之一。
2. Mockito框架簡介
Mockito是一個Mocking框架,可以很容易地為要與您的被測類進行交互的類和接口創(chuàng)建模擬。 Mockito提供了一個非常簡單的API,用于創(chuàng)建模擬并分配其行為。 它使您可以非常快速地指定預期的行為并驗證與模擬的交互。
Mockito本質上具有兩個階段,其中一個或兩個階段都作為單元測試的一部分執(zhí)行:
- 存根
- 驗證
存根是指定模擬行為的過程。 這是我們告訴Mockito與模擬互動時想要發(fā)生的事情的方式。 存根使我們能夠解決我們在第一部分中概述的一些問題–它使為測試創(chuàng)建所有可能的條件變得簡單。 它讓我們控制了模擬的響應,包括強迫它們返回我們想要的任何值,或者拋出我們想要的任何異常。 它使我們可以在不同條件下編寫不同的行為。 存根使我們可以精確控制模擬將執(zhí)行的操作。
驗證是驗證與我們的模擬互動的過程。 它使我們能夠確定模擬的調用方式以及調用的次數(shù)。 它使我們可以查看模擬的參數(shù),以確保它們符合預期。 驗證使我們能夠解決第一部分中提到的其他問題–它使我們確保將我們期望的值準確地傳遞給我們的合作者,并且不會發(fā)生意外情況。 驗證使我們能夠準確確定模擬發(fā)生了什么。
通過將這兩個簡單的階段聯(lián)系在一起,我們可以構建極其靈活和強大的單元測試,使用非常簡單的Mockito API編碼復雜的模擬行為和復雜的模擬交互驗證。
Mockito確實有一些限制,包括
- 你不能嘲笑期末班
- 您不能模擬靜態(tài)方法
- 您不能模擬最終方法
- 您不能模擬equals()或hashCode()
存根的快速示例
想象一下,您正在編寫一個類,該類調用物理溫度傳感器的API。 您要調用double getDegreesC()方法并根據從傳感器返回的值返回以下字符串之一:“ Hot”,“ Mild”,“ Cold”。 至少可以說,要使單元測試控制房間的環(huán)境溫度以測試功能非常困難。 但是,如果我們使用Mockito來創(chuàng)建一個替代傳感器的模擬物怎么辦?
現(xiàn)在我們可以在單元測試中編寫如下代碼:
when(sensor.getDegreesC()).thenReturn(15.0);這告訴Mockito,當模擬傳感器收到對getDegreesC()的調用時,它應該然后返回值15.0。
快速驗證示例
假設您有一個類進行一些計算,并負責在觀察者完成計算后通知觀察者。 您要確保在執(zhí)行方法的過程中一次調用了觀察者的notify()方法。 您可以在觀察器中設置一些布爾值,然后從單元測試中進行檢查,但這意味著更改某些生產代碼,甚至您可能都不擁有這些代碼。 Mockito怎么樣,如果觀察者是模擬的呢?
現(xiàn)在我們可以在單元測試中編寫如下代碼:
verify(observer).notify();這告訴Mockito必須僅一次調用一次notify()方法,否則單元測試將失敗。
3.混合一個Mockito
現(xiàn)在,我們已經了解了一些有關框架的知識,讓我們在項目中使用它。
如果使用Maven,則將Mockito添加到項目中就像添加以下依賴項一樣簡單:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId><version>1.9.5</version><scope>test</scope></dependency>如果您使用Gradle,則只需添加以下內容
dependencies {testCompile "org.mockito:mockito-all:1.9.5" }要將舊版Mockito添加到Eclipse項目的類路徑中,請從Mockito下載頁面中獲取最新的jar(請取名為mmitito-all-1.9.5.jar)并將其下載到硬盤上。
右鍵單擊您的eclipse項目,然后選擇“屬性”,然后在左窗格中選擇“ Java Build Path”,然后在右側選擇“ Libraries”。
在“庫”選項卡上,單擊“添加外部Jar”按鈕,然后導航到您先前下載的模擬所有jar。 選擇罐子,它現(xiàn)在已添加到您的項目中并可供使用。
在撰寫本文時,Mockito的最新版本是1.9.5,但是在將其添加到項目之前,應檢查更新。
4.將Mockito與JUnit一起使用
要將Mockito集成到您的JUnit測試類中,可以使用提供的Test Runner MockitoJUnitRunner 。 只需使用以下注釋您的測試課:
@RunWith(MockitoJUnitRunner.class)這告訴Mockito在測試類中接受所有帶注釋的模擬,并對其進行初始化以進行模擬。 然后,您可以簡單地使用@Mock注釋任何實例變量,以將其用作模擬。 請注意,您應該導入org.mockito.Mock而不是org.mockito.mockitoannotations.Mock ,已棄用。
與所有示例一樣,我們將創(chuàng)建一個新的Test類,并在其中使用Mockito模擬java.util.List :
import java.util.List; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner;@RunWith(MockitoJUnitRunner.class) public class MyTest {@Mockprivate List<String> mockList;}@Mock注釋告訴Mockito模擬mockList將被視為模擬,而@RunWith(MockitoJUnitRunner.class)告訴Mockito遍歷MyTest所有帶有@Mock注釋的成員,并將其初始化以進行模擬。 您不必將任何新實例分配給嘲笑列表,這是由Mockito在后臺完成的。 通過上面的簡單代碼,mockList準備好用作模擬了。
嘗試添加以下導入:
import static org.junit.Assert.*; import static org.mockito.Mockito.*; import org.junit.Test;然后是以下簡單的測試用例:
@Testpublic void test() {String expected = "Hello, World!";when(mockList.get(0)).thenReturn(expected);String actual = mockList.get(0);assertEquals(expected, actual);System.out.println(actual);}在這里,我們看到了我們的期望-我們有一個字符串"Hello, World!" 然后繼續(xù)對模擬列表的List.get()方法進行存根, List.get()在請求列表的第一個元素時返回預期的String。
然后,我們調用mockList.get(0)來獲取測試的實際值,并斷言我們的實際值等于我們的預期值,然后將其輸出到控制臺以進行良好測量。
我們根本沒有創(chuàng)建真實列表,也沒有插入“ Hello,World!” 進入列表。 它只是一個模擬列表,它具有或了解的唯一功能是輸入為0的get()方法。
嘗試更改String actual = mockList.get(0); 到String actual = mockList.get(1); 并運行測試。 您將看到actual值現(xiàn)在為空。 原因是我們唯一保留的功能是使用輸入0調用.get()– Mockito不知道如何使用輸入1進行操作,因此它僅返回null。 實際上,我們調用List的任何其他方法都將返回null,而任何不返回任何值的方法將有效地充當no-op。 這是一個功能強大的控件,在幾行代碼中,我們創(chuàng)建了List的實現(xiàn),該實現(xiàn)恰好在每次調用它時都可以實現(xiàn)我們想要的功能。
5. Mockito最佳做法
Mockito通常鼓勵在單元測試和設計中采用標準的最佳實踐,即:
- Mockito沒有模擬靜態(tài)方法的規(guī)定,因為Mockito鼓勵面向對象的設計和對過程代碼的依賴注入。
- Mockito沒有提供模擬私有方法的規(guī)定,因為公共方法應該是黑盒,并且從測試私有方法的角度來看不存在。
- Mockito打包并鼓勵使用Hamcrest Matchers,這將在后續(xù)教程中介紹。
- Mockito鼓勵遵守Demeter法則,而不鼓勵嘲笑鏈式方法。
- 您不應存根或驗證在不同線程之間共享的模擬。 但是,您可以調用共享模擬的方法。
- 您無法驗證模擬的toString()方法,原因是測試環(huán)境本身可能會調用它,因此無法進行驗證。
- 如果您的測試用例使用了“當時給定”表示法,則可以使用org.mockito.BDDMockito的存根方法,以便when(mock.method()).thenReturn(something)成為given(mock.method()).willReturn(something)因為它將以您的測試格式很好地閱讀。
- 可以在不使用Mockito批注的情況下使用Mockito,但是使用批注更加容易和整潔,這就是我們在這些教程中將要做的。
- 如果您的測試要求您出于測試目的而修改類的特定方法的行為,則可以“監(jiān)視”任何類,包括被測類。 Mockito明確建議僅在偶爾使用間諜時(例如,在處理遺留代碼時受到限制)。 這將在以后的教程中介紹。
- 如果對間諜方法的實際調用可能會產生錯誤條件,或者由于某些其他原因而無法調用,則Mockito建議使用do *系列方法進行存根。 這將在以后的教程中介紹。
- Mockito將允許您使用參數(shù)匹配器代替實際參數(shù),但有一個限制:如果一個參數(shù)使用匹配器,則所有參數(shù)都必須使用匹配器。 參數(shù)匹配器將在以后的教程中介紹,但應謹慎使用。
- Mockito提供了verifyNoMoreInteractions()方法來驗證特定的模擬不再有任何交互,但建議僅在適當?shù)那闆r下謹慎使用。
- Mockito提供了Answer接口以允許使用回調進行存根,但是建議不要使用它,并鼓勵您使用thenReturn()和doThrow()方法進行簡單的存根。 我們將在以后的教程中介紹答案。
- 如果使用ArgumentCaptor進行參數(shù)驗證,則應僅在驗證階段而不是存根階段使用它。 ArgumentCaptor將在以后的教程中介紹。
- Mockito建議非常謹慎地使用Partial Mocks,主要是在處理遺留代碼時。 設計良好的代碼不應要求使用部分模擬。
- Mockito提供了reset()方法,用于在測試方法的中間重置模擬,但是建議您不要使用它,因為它是一種代碼味道,可能會使測試過長和復雜。
有更多功能和做法,但是這些是Mockito告訴您要注意的主要功能和做法。 在接下來的教程中,我們將涵蓋以上所有內容,并進行更深入的介紹。
翻譯自: https://www.javacodegeeks.com/2015/11/getting-started-with-mockito.html
總結
- 上一篇: 身份证水印怎么加安全 身份证水印如何加安
- 下一篇: 何陋之有的之是什么意思(何陋之有的解释及