日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務,提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747



為什么要mock?

有很多朋友不愿意寫單元測試,覺得寫單測試比較花時間,甚至不會寫單元測試,很大程度上是因為不想寫或者不會寫mock。

mock對于單元測試來很重要。單元測試之所以名字里面有“單元”,就是因為一個測試用例只測很小的一個單元代碼。但我們的代碼總會有依賴,要測試的方法內(nèi)部可能調(diào)用了其它類的方法,這在代碼中是很常見的邏輯。所以我們經(jīng)常會需要mock,用來消除依賴。

所謂mock,翻譯過來就是“模擬”,就是模擬你要測試的代碼里面「依賴的其它對象」,模擬它的輸入和輸出,這樣就不用管其它邏輯是如何實現(xiàn)的,我們「假定它是符合我們望的就行了」

可能你會有一個疑問:那我們在單元測試里面假定它符合期望了,但實際運行時它有bug了,并不是符合我們期望的,怎么辦呢?

首先,我們對那個類也會做單元測試,我們用它的單元測試保證了它的邏輯是正確的。

然后,一個完整的測試體系,不應該只有單元測試。單元測試之上,還應該有集成測試、API測試等,從更高的層面來保證整個應用程序能夠如預期工作。

Mockito的基本用法

Mockito是JAVA語言非常主流的一個框架,自己使用起來感覺也比較好用。所以這篇文章想?yún)R總介紹一下Mockito的各種用法,這樣大家在以后寫單元測試和看單元測試的時候,就能夠比較清晰為什么要這么寫。

設(shè)置Mockito環(huán)境

要使用Mockito,首先得在測試類里面設(shè)置好Mockito環(huán)境。這是為了能夠讓單元測試框架(本文主要介紹JUnit)能夠識別和使用Mockito。

在JUnit 4, JUnit使用了@RunWith注解來聲明一個“運行器”。這個運行期的作用是為單元測試提供「mock的初始化工作」(比如使用@Mock、@Spy等注解時,需要初始化),以及「驗證mock語法」的功能。

比如我們可能會經(jīng)常用到的:

@RunWith(JUnit4.class)
@RunWith(SpringRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)

Mockito也有相應的啟動器,在@RunWith注解上面使用這個啟動器就可以使用Mockito的環(huán)境了:

@RunWith(MockitoJUnitRunner)

在JUnit 5,使用了@ExtendWith注解來代替@RunWith注解,Mockito也支持JUnit 5,提供了MockitoExtension類。

除了使用注解以外,也可以使用靜態(tài)方法initMocks來實現(xiàn)這個功能:

MockitoAnnotations.initMocks(this) 

mock

mock,即mock一個對象。也是注解和代碼兩種方式可以實現(xiàn)。

@Mock
private User user;

Order order = Mockito.mock(Order.class);

mock對象后,就可以對它使用given等方法模擬它的輸入和輸出。

given(order.getId()).willReturn(1L);
assertEquals(order.getId(), 1L);

spy

mock出來的對象是完全虛擬的,不會真正地調(diào)用本來的實現(xiàn)。如果不對它使用given等方法,會返回默認值(null, 0, false等)。而spy如果不使用given等方法,會調(diào)用這個對象本來的實現(xiàn),返回實際運行后的值。

?

不是很推薦使用spy,因為它沒有消除依賴

?

spy同樣有注解和靜態(tài)方法的方式:

@Spy
private user user;

Order order = Mockito.spy(Order.class);

captor

captor翻譯過來是“捕獲”的意思,主要用來捕捉程序運行時調(diào)用mock或者spy的對象的方法時,傳入的參數(shù)。它支持泛型,即要捕獲的參數(shù)的類型。

@Captor 
ArgumentCaptor<User> userCaptor;

ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);

captor一般是與given或者verify等方法配合使用。

@Mock
List mockedList;
 
@Captor 
ArgumentCaptor argCaptor;

@Test
public void whenUseCaptorAnnotation_thenTheSam() {
    mockedList.add("one");
    Mockito.verify(mockedList).add(argCaptor.capture());
 
    assertEquals("one", argCaptor.getValue());
}

InjectMocks

使用@InjectMocks注解,可以將mock或spy的對象自動注入要測試的對象。這在Spring等使用自動注入的框架里用得非常廣泛。

@Mock
Map<String, String> wordMap;
 
@InjectMocks
MyDictionary dic = new MyDictionary();

// 類定義:
class MyDictionary {
    Map<String, String> wordMap;
 
    public MyDictionary() {
        wordMap = new HashMap<String, String>();
    }
    public void add(final String word, final String meaning) {
        wordMap.put(word, meaning);
    }
    public String getMeaning(final String word) {
        return wordMap.get(word);
    }
}

打樁

所謂打樁,其實就是mock的一個過程。我們給定期望的輸入和輸入,mock的對象就能夠如我們期望的那樣工作。

打樁有兩種寫法,一種是傳統(tǒng)的寫法,一種是BDD **Behavior-Driven Development (行為驅(qū)動開發(fā))**的寫法。

傳統(tǒng)寫法

我們先看看傳統(tǒng)的寫法,傳統(tǒng)的寫法主要用Mockito類的方法。基本是when-then-invoke-verify形式。

when(phoneBookRepository.contains(momContactName))
  .thenReturn(false);
 
phoneBookService.register(momContactName, momPhoneNumber);
 
verify(phoneBookRepository)
  .insert(momContactName, momPhoneNumber);

BDD寫法

BDD寫法主要是用BDDMockito類的方法,看起來是given–will-invoke-then的形式。

given(phoneBookRepository.contains(momContactName))
  .willReturn(false);
 
phoneBookService.register(momContactName, momPhoneNumber);
 
then(phoneBookRepository)
  .should()
  .insert(momContactName, momPhoneNumber);

動態(tài)mock

有時候我們可能會遇到這個問題:在方法內(nèi)部可能有循環(huán)或者遞歸,會多次調(diào)用其它對象的方法,但輸入的參數(shù)不同,我們在mock的時候,期望它根據(jù)不同的輸入?yún)?shù)返回不同的結(jié)果。這個時候如果一個一個寫given,就需要寫很多次。但其實可以用動態(tài)mock來做。它是基于InvocationOnMock來實現(xiàn)的。

given(phoneBookRepository.contains(momContactName))
  .willReturn(true);

given(phoneBookRepository.getPhoneNumberByContactName(momContactName))
  .will((InvocationOnMock invocation) ->
    invocation.getArgument(0).equals(momContactName) 
      ? momPhoneNumber 
      : null);

phoneBookService.search(momContactName);

then(phoneBookRepository)
  .should()
  .getPhoneNumberByContactName(momContactName);

Mockito的不足

Mockito可以說是Java語言最流行的mock框架了。但它不是所有對象都可以mock的,有一些限制。

Mockito在3.4.0以前,是不能mock靜態(tài)方法的。這取決于它的底層實現(xiàn),是使用動態(tài)代理來做的。而動態(tài)代理是代理靜態(tài)方法、final方法和private方法的。

一般來說,我們不應該mock靜態(tài)方法、final方法和private方法的。但有時候可能會有這樣的需求,比如Apache和guava框架,就使用了大量的靜態(tài)方法提供一些工具類。如果需要Mock的話,就得配合PowerMock等框架來實現(xiàn)。但PowerMock框架目前還不支持JUnit 5。

在Mockito 3.4.0,開始支持mock靜態(tài)方法,底層是通過修改字節(jié)碼來實現(xiàn)的。但性能很差,mock一個實例大概要一秒多,大家酌情使用。

關(guān)于作者

我是Yasin,一個有顏有料又有趣的程序員。

微信公眾號:編了個程

個人網(wǎng)站:https://yasinshaw.com

分享到:
標簽:mockito
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定