成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

手把手教你 Mockito 的使用

_DangJin / 499人閱讀

摘要:什么是是一個強(qiáng)大的用于開發(fā)的模擬測試框架通過我們可以創(chuàng)建和配置對象進(jìn)而簡化有外部依賴的類的測試使用的大致流程如下創(chuàng)建外部依賴的對象然后將此對象注入到測試類中執(zhí)行測試代碼校驗(yàn)測試代碼是否執(zhí)行正確為什么使用我們已經(jīng)知道了主要的功能就是創(chuàng)建對象那

什么是 Mockito

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

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

執(zhí)行測試代碼.

校驗(yàn)測試代碼是否執(zhí)行正確.

為什么使用 Mockito

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

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

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

Mockito 基本使用

為了簡潔期間, 下面的代碼都省略了靜態(tài)導(dǎo)入 import static org.mockito.Mockito.*;

Maven 依賴

    org.mockito
    mockito-core
    2.0.111-beta
創(chuàng)建 Mock 對象
@Test
public void createMockObject() {
    // 使用 mock 靜態(tài)方法創(chuàng)建 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);
}

如上代碼所示, 我們調(diào)用了 mock 靜態(tài)方法來創(chuàng)建一個 Mock 對象. mock 方法接收一個 class 類型, 即我們需要 mock 的類型.

配置 Mock 對象

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

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

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

    Assert.assertTrue(mockedList.add("one"));
    // 因?yàn)槲覀儧]有定制 add("two"), 因此返回默認(rèn)值, 即 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)" 表示: 當(dāng)調(diào)用了mockedList.add("one"), 那么返回 true.. 并且要注意的是, when(?...).thenReturn(?...) 方法鏈不僅僅要匹配方法的調(diào)用, 而且要方法的參數(shù)一樣才行.
而且有趣的是, when(?...).thenReturn(?...) 方法鏈可以指定多個返回值, 當(dāng)這樣做后, 如果多次調(diào)用指定的方法, 那么這個方法會依次返回這些值. 例如 "when(i.next()).thenReturn("Hello,").thenReturn("Mockito!");", 這句代碼表示: 第一次調(diào)用 i.next() 時返回 "Hello,", 第二次調(diào)用 i.next() 時返回 "Mockito!".

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

@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
}

上面代碼的第一第二步我們已經(jīng)很熟悉了, 接下來第三部我們使用了一個新語法: doThrow(ExceptionX).when(x).methodCall, 它的含義是: 當(dāng)調(diào)用了 x.methodCall 方法后, 拋出異常 ExceptionX.
因此 doThrow(new NoSuchElementException()).when(i).next() 的含義就是: 當(dāng)?shù)谌握{(diào)用 i.next() 后, 拋出異常 NoSuchElementException.(因?yàn)?i 這個迭代器只有兩個元素)

校驗(yàn) Mock 對象的方法調(diào)用

Mockito 會追蹤 Mock 對象的所用方法調(diào)用和調(diào)用方法時所傳遞的參數(shù). 我們可以通過 verify() 靜態(tài)方法來來校驗(yàn)指定的方法調(diào)用是否滿足斷言. 語言描述有一點(diǎn)抽象, 下面我們?nèi)匀灰源a來說明一下.

@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();
}

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

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

讀者根據(jù)代碼也應(yīng)該可以猜測出它的含義了, 很簡單:

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

第二句校驗(yàn) mockedList.add("two") 被調(diào)用了 1 次(times(1))

第三句校驗(yàn) mockedList.add("three times") 被調(diào)用了 3 次(times(3))

第四句校驗(yàn) mockedList.isEmpty() 從未被調(diào)用(never)

使用 spy() 部分模擬對象

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

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

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

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

    // 因?yàn)槲覀儧]有對 get(0), get(1) 方法進(jìn)行定制,
    // 因此這些調(diào)用其實(shí)是調(diào)用的真實(shí)對象的方法.
    Assert.assertEquals(spy.get(0), "one");
    Assert.assertEquals(spy.get(1), "two");

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

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

參數(shù)捕獲

Mockito 允準(zhǔn)我們捕獲一個 Mock 對象的方法調(diào)用所傳遞的參數(shù), 例如:

@Test
public void testCaptureArgument() {
    List list = Arrays.asList("1", "2");
    List mockedList = mock(List.class);
    ArgumentCaptor 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 方法所傳遞的實(shí)參 list.

本文由 yongshun 發(fā)表于個人博客, 采用署名-非商業(yè)性使用-相同方式共享 3.0 中國大陸許可協(xié)議.
非商業(yè)轉(zhuǎn)載請注明作者及出處. 商業(yè)轉(zhuǎn)載請聯(lián)系作者本人
Email: [email protected]
本文標(biāo)題為: 手把手教你 Mockito 的使用
本文鏈接為: https://segmentfault.com/a/11...

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65076.html

相關(guān)文章

  • 把手教你學(xué)Dapr

    摘要:配置配置使用概率抽樣。采樣率定義了對跟蹤跨度進(jìn)行采樣的概率,其值可以介于和含之間。例如,以下配置對象將采樣率更改為即每個跨度都被采樣,并使用協(xié)議將跟蹤發(fā)送到位于的服務(wù)器文件路徑注將采樣率更改為會完全禁用跟蹤。目錄手把手教你學(xué)Dapr - 1. .Net開發(fā)者的大時代手把手教你學(xué)Dapr - 2. 必須知道的概念手把手教你學(xué)Dapr - 3. 使用Dapr運(yùn)行第一個.Net程序手把手教你學(xué)Da...

    qqlcbb 評論0 收藏0
  • 把手教你寫電商爬蟲-第五課 京東商品評論爬蟲 一起來對付反爬蟲

    摘要:和前面幾節(jié)課類似的分析這節(jié)課就不做了,對于分頁,請求什么的,大家可以直接參考前面的四節(jié)課,這一刻主要特別的是,我們在采集商品的同時,會將京東的商品評價采集下來。 系列教程: 手把手教你寫電商爬蟲-第一課 找個軟柿子捏捏 手把手教你寫電商爬蟲-第二課 實(shí)戰(zhàn)尚妝網(wǎng)分頁商品采集爬蟲 手把手教你寫電商爬蟲-第三課 實(shí)戰(zhàn)尚妝網(wǎng)AJAX請求處理和內(nèi)容提取 手把手教你寫電商爬蟲-第四課 淘寶網(wǎng)商品爬...

    jsummer 評論0 收藏0
  • 把手教你寫電商爬蟲-第五課 京東商品評論爬蟲 一起來對付反爬蟲

    摘要:和前面幾節(jié)課類似的分析這節(jié)課就不做了,對于分頁,請求什么的,大家可以直接參考前面的四節(jié)課,這一刻主要特別的是,我們在采集商品的同時,會將京東的商品評價采集下來。 系列教程: 手把手教你寫電商爬蟲-第一課 找個軟柿子捏捏 手把手教你寫電商爬蟲-第二課 實(shí)戰(zhàn)尚妝網(wǎng)分頁商品采集爬蟲 手把手教你寫電商爬蟲-第三課 實(shí)戰(zhàn)尚妝網(wǎng)AJAX請求處理和內(nèi)容提取 手把手教你寫電商爬蟲-第四課 淘寶網(wǎng)商品爬...

    forsigner 評論0 收藏0
  • 把手教你基于WordPress搭建自己個人博客

    摘要:一步一步教你基于搭建自己的個人博客,作為成熟的框架,美觀,方便,插件多,更新頻繁,非常適合個人博客與網(wǎng)站的搭建,適合新手,無需太多的代碼基礎(chǔ)。原文鏈接手把手教你搭建自己的網(wǎng)站購買購買云服務(wù)器為了搭建個人網(wǎng)站,首先肯定需要一個云服務(wù)器。 一步一步教你基于WordPress搭建自己的個人博客,WordPress作為成熟的CMS框架,美觀,方便,插件多,更新頻繁,非常適合個人博客與網(wǎng)站的搭建...

    vpants 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<