日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

手把手教你 Mockito 的使用

發布時間:2023/12/31 综合教程 29 生活家
生活随笔 收集整理的這篇文章主要介紹了 手把手教你 Mockito 的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

什么是 Mockito

Mockito 是一個強大的用于 Java 開發的模擬測試框架, 通過 Mockito 我們可以創建和配置 Mock 對象, 進而簡化有外部依賴的類的測試.
使用 Mockito 的大致流程如下:

創建外部依賴的 Mock 對象, 然后將此 Mock 對象注入到測試類中.

執行測試代碼.

校驗測試代碼是否執行正確.

為什么使用 Mockito

我們已經知道了 Mockito 主要的功能就是創建 Mock 對象, 那么什么是 Mock 對象呢? 對 Mock 對象不是很了解的朋友, 可以參考這篇文章.
現在我們對 Mock 對象有了一定的了解了, 那么自然就會有人問了, 為什么要使用 Mock 對象? 使用它有什么好處呢?
下面我們以一個簡單的例子來展示一下 Mock 對象到底有什么用.
假設我們正在編寫一個銀行的服務 BankService, 這個服務的依賴關系如下:

當我們需要測試 BankService 服務時, 該真么辦呢?
一種方法是構建真實的 BankDao, DB, AccountService 和 AuthService 實例, 然后注入到 BankService 中.
不用我說, 讀者們也肯定明白, 這是一種既笨重又繁瑣的方法, 完全不符合單元測試的精神. 那么還有一種更加優雅的方法嗎? 自然是有的, 那就是我們今天的主角Mock Object. 下面來看一下使用 Mock 對象后的框架圖:

我們看到, BankDao, AccountService 和 AuthService 都被我們使用了虛擬的對象(Mock 對象) 來替換了, 因此我們就可以對 BankService 進行測試, 而不需要關注它的復雜的依賴了.

Mockito 基本使用

為了簡潔期間, 下面的代碼都省略了靜態導入import static org.mockito.Mockito.*;

Maven 依賴

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.0.111-beta</version>
</dependency>

創建 Mock 對象

@Test
public void createMockObject() {
    // 使用 mock 靜態方法創建 Mock 對象.
    List mockedList = mock(List.class);
    Assert.assertTrue(mockedList instanceof List);

    // mock 方法不僅可以 Mock 接口類, 還可以 Mock 具體的類型.
    ArrayList mockedArrayList = mock(ArrayList.class);
    Assert.assertTrue(mockedArrayList instanceof List);
    Assert.assertTrue(mockedArrayList instanceof ArrayList);
}

如上代碼所示, 我們調用了mock靜態方法來創建一個 Mock 對象. mock 方法接收一個 class 類型, 即我們需要 mock 的類型.

配置 Mock 對象

當我們有了一個 Mock 對象后, 我們可以定制它的具體的行為. 例如:

@Test
public void configMockObject() {
    List mockedList = mock(List.class);

    // 我們定制了當調用 mockedList.add("one") 時, 返回 true
    when(mockedList.add("one")).thenReturn(true);
    // 當調用 mockedList.size() 時, 返回 1
    when(mockedList.size()).thenReturn(1);

    Assert.assertTrue(mockedList.add("one"));
    // 因為我們沒有定制 add("two"), 因此返回默認值, 即 false.
    Assert.assertFalse(mockedList.add("two"));
    Assert.assertEquals(mockedList.size(), 1);

    Iterator i = mock(Iterator.class);
    when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");
    String result = i.next() + " " + i.next();
    //assert
    Assert.assertEquals("Hello, Mockito!", result);
}

我們使用when(?...).thenReturn(?...)方法鏈來定義一個行為, 例如 "when(mockedList.add("one")).thenReturn(true)" 表示:當調用了mockedList.add("one"), 那么返回 true.. 并且要注意的是,when(?...).thenReturn(?...)方法鏈不僅僅要匹配方法的調用, 而且要方法的參數一樣才行.
而且有趣的是,when(?...).thenReturn(?...)方法鏈可以指定多個返回值, 當這樣做后, 如果多次調用指定的方法, 那么這個方法會依次返回這些值. 例如 "when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");", 這句代碼表示: 第一次調用 i.next() 時返回 "Hello,", 第二次調用 i.next() 時返回 "Mockito!".

上面的例子我們展示了方法調用返回值的定制, 那么我們可以指定一個拋出異常嗎? 當然可以的, 例如:

@Test(expected = NoSuchElementException.class)
public void testForIOException() throws Exception {
    Iterator i = mock(Iterator.class);
    when(i.next()).thenReturn("Hello,").thenReturn("Mockito!"); // 1
    String result = i.next() + " " + i.next(); // 2
    Assert.assertEquals("Hello, Mockito!", result);

    doThrow(new NoSuchElementException()).when(i).next(); // 3
    i.next(); // 4
}

上面代碼的第一第二步我們已經很熟悉了, 接下來第三部我們使用了一個新語法:doThrow(ExceptionX).when(x).methodCall, 它的含義是: 當調用了 x.methodCall 方法后, 拋出異常 ExceptionX.
因此 doThrow(new NoSuchElementException()).when(i).next() 的含義就是: 當第三次調用 i.next() 后, 拋出異常 NoSuchElementException.(因為 i 這個迭代器只有兩個元素)

校驗 Mock 對象的方法調用

Mockito 會追蹤 Mock 對象的所用方法調用和調用方法時所傳遞的參數. 我們可以通過 verify() 靜態方法來來校驗指定的方法調用是否滿足斷言. 語言描述有一點抽象, 下面我們仍然以代碼來說明一下.

@Test
public void testVerify() {
    List mockedList = mock(List.class);
    mockedList.add("one");
    mockedList.add("two");
    mockedList.add("three times");
    mockedList.add("three times");
    mockedList.add("three times");
    when(mockedList.size()).thenReturn(5);
    Assert.assertEquals(mockedList.size(), 5);

    verify(mockedList, atLeastOnce()).add("one");
    verify(mockedList, times(1)).add("two");
    verify(mockedList, times(3)).add("three times");
    verify(mockedList, never()).isEmpty();
}

上面的例子前半部份沒有什么特別的, 我們關注后面的:

verify(mockedList, atLeastOnce()).add("one");
verify(mockedList, times(1)).add("two");
verify(mockedList, times(3)).add("three times");
verify(mockedList, never()).isEmpty();

讀者根據代碼也應該可以猜測出它的含義了, 很簡單:

第一句校驗 mockedList.add("one") 至少被調用了 1 次(atLeastOnce)

第二句校驗 mockedList.add("two") 被調用了 1 次(times(1))

第三句校驗 mockedList.add("three times") 被調用了 3 次(times(3))

第四句校驗 mockedList.isEmpty() 從未被調用(never)

使用 spy() 部分模擬對象

Mockito 提供的 spy 方法可以包裝一個真實的 Java 對象, 并返回一個包裝后的新對象. 若沒有特別配置的話, 對這個新對象的所有方法調用, 都會委派給實際的 Java 對象. 例如:

@Test
public void testSpy() {
    List list = new LinkedList();
    List spy = spy(list);

    // 對 spy.size() 進行定制.
    when(spy.size()).thenReturn(100);

    spy.add("one");
    spy.add("two");

    // 因為我們沒有對 get(0), get(1) 方法進行定制,
    // 因此這些調用其實是調用的真實對象的方法.
    Assert.assertEquals(spy.get(0), "one");
    Assert.assertEquals(spy.get(1), "two");

    Assert.assertEquals(spy.size(), 100);
}

這個例子中我們實例化了一個 LinkedList 對象, 然后使用 spy() 方法對 list 對象進行部分模擬. 接著我們使用when(...).thenReturn(...)方法鏈來規定 spy.size() 方法返回值是 100. 隨后我們給 spy 添加了兩個元素, 然后再 調用 spy.get(0) 獲取第一個元素.
這里有意思的地方是: 因為我們沒有定制 add("one"), add("two"), get(0), get(1), 因此通過 spy 調用這些方法時, 實際上是委派給 list 對象來調用的.
然而我們 定義了 spy.size() 的返回值, 因此當調用 spy.size() 時, 返回 100.

參數捕獲

Mockito 允準我們捕獲一個 Mock 對象的方法調用所傳遞的參數, 例如:

@Test
public void testCaptureArgument() {
    List<String> list = Arrays.asList("1", "2");
    List mockedList = mock(List.class);
    ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
    mockedList.addAll(list);
    verify(mockedList).addAll(argument.capture());

    Assert.assertEquals(2, argument.getValue().size());
    Assert.assertEquals(list, argument.getValue());
}

我們通過 verify(mockedList).addAll(argument.capture()) 語句來獲取 mockedList.addAll 方法所傳遞的實參 list.

總結

以上是生活随笔為你收集整理的手把手教你 Mockito 的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。