溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在Java中使用Mockito測試框架

發布時間:2021-06-09 18:14:21 來源:億速云 閱讀:249 作者:Leah 欄目:編程語言

本篇文章給大家分享的是有關如何在Java中使用Mockito測試框架,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

什么是 Mock 測試

Mock 測試就是在測試過程中,對于某些不容易構造(如 HttpServletRequest 必須在Servlet 容器中才能構造出來)或者不容易獲取比較復雜的對象(如 JDBC 中的ResultSet 對象),用一個虛擬的對象(Mock 對象)來創建以便測試的測試方法。

Mock 最大的功能是幫你把單元測試的耦合分解開,如果你的代碼對另一個類或者接口有依賴,它能夠幫你模擬這些依賴,并幫你驗證所調用的依賴的行為。

比如一段代碼有這樣的依賴:

如何在Java中使用Mockito測試框架

當我們需要測試A類的時候,如果沒有 Mock,則我們需要把整個依賴樹都構建出來,而使用 Mock 的話就可以將結構分解開,像下面這樣:

如何在Java中使用Mockito測試框架

Mock 對象使用范疇

真實對象具有不可確定的行為,產生不可預測的效果(如:股票行情,天氣預報) :

  • 真實對象很難被創建的

  • 真實對象的某些行為很難被觸發

  • 真實對象實際上還不存在的(和其他開發小組或者和新的硬件打交道)等等

使用 Mock 對象測試的關鍵步驟

  • 使用一個接口來描述這個對象

  • 在產品代碼中實現這個接口

  • 在測試代碼中實現這個接口

  • 在被測試代碼中只是通過接口來引用對象,所以它不知道這個引用的對象是真實對象,還是 Mock 對象。

Mock 與Stub 的區別

Mock 不是 Stub,兩者是有區別的:

  • 前者被稱為 mockist TDD,而后者一般稱為 classic TDD ;

  • 前者是基于行為的驗證(behavior verification),后者是基于狀態的驗證 (state verification);

  • 前者使用的是模擬的對象,而后者使用的是真實的對象。

Java Mock 測試

目前,在 Java 陣營中主要的 Mock 測試工具有 Mockito,JMock,EasyMock 等。

關于這些框架的比較,不是本文的重點。本文著重介紹 Mockito 的使用。

Mockito 的特性

Mockito 是美味的 Java 單元測試 Mock 框架,開源。

大多 Java Mock 庫如 EasyMock 或 JMock 都是 expect-run-verify (期望-運行-驗證)方式,而 Mockito 則使用更簡單,更直觀的方法:在執行后的互動中提問。使用 Mockito,你可以驗證任何你想要的。而那些使用 expect-run-verify 方式的庫,你常常被迫查看無關的交互。

非 expect-run-verify 方式 也意味著,Mockito 無需準備昂貴的前期啟動。他們的目標是透明的,讓開發人員專注于測試選定的行為。

Mockito 擁有的非常少的 API,所有開始使用 Mockito,幾乎沒有時間成本。因為只有一種創造 mock 的方式。只要記住,在執行前 stub,而后在交互中驗證。你很快就會發現這樣 TDD java 代碼是多么自然。

類似 EasyMock 的語法來的,所以你可以放心地重構。Mockito 并不需要“expectation(期望)”的概念。只有 stub 和驗證。

Mockito 實現了 Gerard Meszaros 所謂的 Test Spy.

其他的一些特點:

  • 可以 mock 具體類而不單止是接口

  • 一點注解語法糖 - @Mock

  • 干凈的驗證錯誤是 - 點擊堆棧跟蹤,看看在測試中的失敗驗證;點擊異常的原因來導航到代碼中的實際互動。堆棧跟蹤總是干干凈凈。

  • 允許靈活有序的驗證(例如:你任意有序 verify,而不是每一個單獨的交互)

  • 支持“詳細的用戶號碼的時間”以及“至少一次”驗證

  • 靈活的驗證或使用參數匹配器的 stub (anyObject(),anyString() 或 refEq() 用于基于反射的相等匹配)

  • 允許創建自定義的參數匹配器或者使用現有的 hamcrest 匹配器

Mockito 入門

聲明 mockito 依賴

Gradle 用戶可以使用:

repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }

示例

1.驗證行為

//Let's import Mockito statically so that the code looks clearer
import static org.mockito.Mockito.*;
//mock creation
List mockedList = mock(List.class);
//using mock object
mockedList.add("one");
mockedList.clear();
//verification
verify(mockedList).add("one");
verify(mockedList).clear();

一旦創建 mock 將會記得所有的交互。你可以選擇驗證你感興趣的任何交互

2.stubbing

//You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);
//stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());
//following prints "first"
System.out.println(mockedList.get(0));
//following throws runtime exception
System.out.println(mockedList.get(1));
//following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));
//Although it is possible to verify a stubbed invocation, usually it's just redundant
//If your code cares what get(0) returns, then something else breaks (often even before verify() gets executed).
//If your code doesn't care what get(0) returns, then it should not be stubbed. Not convinced? See here.
verify(mockedList).get(0);
  • 默認情況下,所有方法都會返回值,一個 mock 將返回要么 null,一個原始/基本類型的包裝值或適當的空集。例如,對于一個 int/Integer 就是 0,而對于 boolean/Boolean 就是 false。

  • Stubbing 可以被覆蓋。

  • 一旦 stub,該方法將始終返回一個 stub 的值,無論它有多少次被調用。

  • 最后的 stubbing 是很重要的 - 當你使用相同的參數 stub 多次同樣的方法。換句話說:stubbing 的順序是重要的,但它唯一有意義的卻很少,例如當 stubbing 完全相同的方法調用,或者有時當參數匹配器的使用,等等。

3.參數匹配器

Mockito 驗證參數值使用 Java 方式:通過使用 equals() 方法。有時,當需要額外的靈活性,可以使用參數匹配器:

//stubbing using built-in anyInt() argument matcher
when(mockedList.get(anyInt())).thenReturn("element");
//stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
when(mockedList.contains(argThat(isValid()))).thenReturn("element");
//following prints "element"
System.out.println(mockedList.get(999));
//you can also verify using an argument matcher
verify(mockedList).get(anyInt());

參數匹配器允許靈活的驗證或 stubbing。點擊這里查看更多內置的匹配器和自定義的參數匹配器/ hamcrest匹配器的例子。

自定義參數的匹配信息,請查看 Javadoc 中 ArgumentMatcher 類。

如果你正在使用參數的匹配,所有的參數都由匹配器來提供。

下面的示例演示驗證,但同樣適用于 stubbing:

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher
verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument is given without an argument matcher.

4.調用額外的調用數字/at least x / never

//using mock
mockedList.add("once");
mockedList.add("twice");
mockedList.add("twice");
mockedList.add("three times");
mockedList.add("three times");
mockedList.add("three times");
//following two verifications work exactly the same - times(1) is used by default
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once");
//exact number of invocations verification
verify(mockedList, times(2)).add("twice");
verify(mockedList, times(3)).add("three times");
//verification using never(). never() is an alias to times(0)
verify(mockedList, never()).add("never happened");
//verification using atLeast()/atMost()
verify(mockedList, atLeastOnce()).add("three times");
verify(mockedList, atLeast(2)).add("five times");
verify(mockedList, atMost(5)).add("three times");

times(1) 是默認的,因此,使用的 times(1) 可以顯示的省略。

5.Stubbing void 方法處理異常

doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();

6.有序的驗證

// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);
//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");
//create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock);
//following will make sure that add is first called with "was added first, then with "was added second"
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");
// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);
//using mocks
firstMock.add("was called first");
secondMock.add("was called second");
//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);
//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");
// Oh, and A + B can be mixed together at will

有序驗證是為了靈活 - 你不必一個接一個驗證所有的交互。

此外,您還可以通過創建 InOrder 對象傳遞只與有序驗證相關的 mock 。

7. 確保 mock 上不會發生交互

//using mocks - only mockOne is interacted
mockOne.add("one");
//ordinary verification
verify(mockOne).add("one");
//verify that method was never called on a mock
verify(mockOne, never()).add("two");
//verify that other mocks were not interacted
verifyZeroInteractions(mockTwo, mockThree);

8.尋找多余的調用

//using mocks
mockedList.add("one");
mockedList.add("two");
verify(mockedList).add("one");
//following verification will fail
verifyNoMoreInteractions(mockedList);

注意:不建議 verifyNoMoreInteractions() 在每個測試方法中使用。 verifyNoMoreInteractions() 是從交互測試工具包一個方便的斷言。只有與它的相關時才使用它。濫用它導致難以維護。

9. 標準創建 mock 方式 - 使用 @Mock 注解

  • 最小化可重用 mock 創建代碼

  • 使測試類更加可讀性

  • 使驗證錯誤更加易讀,因為字段名稱用于唯一識別 mock

public class ArticleManagerTest {
@Mock private ArticleCalculator calculator;
@Mock private ArticleDatabase database;
@Mock private UserProvider userProvider;
private ArticleManager manager;

在基礎類或者測試 runner 里面,使用如下:

MockitoAnnotations.initMocks(testClass);

可以使用內建 runner: MockitoJUnitRunner 或者 rule: MockitoRule

10. Stubbing 連續調用(迭代器式的 stubbing)

when(mock.someMethod("some arg"))
.thenThrow(new RuntimeException())
.thenReturn("foo");
//First call: throws runtime exception:
mock.someMethod("some arg");
//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));
//Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));

下面是一個精簡版本:

when(mock.someMethod("some arg"))
.thenReturn("one", "two", "three");

11. 回調 Stubbing

允許使用泛型 Answer 接口。

然而,這是不包括在最初的 Mockito 另一個有爭議的功能。我們建議您只需用thenReturn() 或 thenThrow() 來 stubbing ,這在測試/測試驅動中應用簡潔與簡單的代碼足夠了。但是,如果你有一個需要 stub 到泛型 Answer 接口,這里是一個例子:

when(mock.someMethod(anyString())).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return "called with arguments: " + args;
}
});
//the following prints "called with arguments: foo"
System.out.println(mock.someMethod("foo"));

12. doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod() 家族方法

Stubbing void 方法,需要不同的 when(Object) ,因為編譯器不喜歡括號內無效的方法…

在 用于 Stubbing void 方法中,doThrow(Throwable…) 取代 stubVoid(Object)。主要原因是提高可讀性和與 doAnswer() 保持一致性。

當你想用 stub void 方法 使用 doThrow():

doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();

在調用 when() 的相應地方可以使用 oThrow(), doAnswer(), doNothing(), doReturn() 和 doCallRealMethod(),當:

  • stub void 方法

  • stub 方法在 spy 對象(見下面)

  • 可以不止一次的 stub 相同的方法,在測試的中期來改變 mock 的行為

但你更加傾向于使用這些方法來代替 when(),在所有的 stubbing 調用。

以上就是如何在Java中使用Mockito測試框架,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女