您的位置:軟件測試 > 開源軟件測試 > 開源單元測試工具 > junit
JUnit + Mockito單元測試
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時間:[ 2016/12/29 11:43:39 ] 推薦標(biāo)簽:單元測試 Junit

  再靜態(tài)導(dǎo)入 org.mockito.Mockito.*; 里的靜態(tài)方法,這樣能在測試方法進(jìn)行對象的 Mock。Mockito 支持通過靜態(tài)方法 mock() 來 Mock 對象,或者通過 @Mock 注解,來創(chuàng)建 Mock 對象,但必須將其實例化。先演示下如何 Mock 對象:
  import static org.mockito.Mockito.*;
  @Test
  publicvoidmockIterator(){
  Iterator i = mock(Iterator.class);
  when(i.next()).thenReturn("hello").thenReturn("world");
  String result = i.next() + " " + i.next();
  assertEquals("hello world", result);
  }
  mock 出來的對象擁有和源對象同樣的方法和屬性, when() 和 thenReturn() 方法是對源對象的配置,怎么理解,是說在第一步 mock() 時,mock 出來的對象還不具備被 Mock 對象實例的行為特征,而 when(...).thenReturn(...) 是根據(jù)條件去配置源對象的預(yù)期行為,即:當(dāng)執(zhí)行 when() 中的操作時,返回 thenReturn() 中的結(jié)果。比如上面的代碼中,mock 出來的 i 實例在被遍歷時會依次輸出 “hello” 和 “world”, assertEquals() 是對預(yù)期結(jié)果和實際結(jié)果的判斷。
  同理,也可以 Mock 網(wǎng)絡(luò)請求,比如 HttpServletRequest 里的參數(shù),也可以通過上面的方式來設(shè)定被 Mock 的源對象的表現(xiàn)行為。
  對于 when() 不定條件,Mockito 定義了 any() 、 anyInt() 、 anyString() 、 anySet() 等方法來匹配指定類型的不定輸入, anyInt() 匹配 int 參數(shù), anyString() 匹配 String 參數(shù), any() 匹配 任意類型的參數(shù)。如果需要匹配自定義的類型,可以通過 any(CustomedClass.class) 來配置。
  thenReturn() 返回的是一個確定值,這在模擬可見的行為時是沒問題的,但有時候,我們需要得到一個復(fù)雜的不定輸出的行為,比如返回一個回調(diào)方法,或者返回一個類實例,Mockito 可以通過 thenAnswer() 來實現(xiàn)。參考 StackOverflow 上的這篇問答 Mockito : thenAnswer Vs thenReturn 。
  @Test
  publicvoidcount()throwsException{
  Duplicator counter = mock(Counter.class);
  Answer<Integer> answer = new Answer<Integer>() {
  publicIntegeranswer(InvocationOnMock invocationOnMock)throwsThrowable{
  return ((String) invocationOnMock.getArguments()[0]).length();
  }
  };
  when(counter.count(anyString())).thenAnswer(answer);
  }
  InvocationOnMock 接口提供了獲取被測試方法的調(diào)用信息的幾個重要方法:
  getMock() 接口返回 mock 對象;
  getMethod() 接口返回被調(diào)用方法的 Method 對象;
  getArguments() 接口返回被測試方法的入?yún)⒘斜恚?br />   getArgument() 接口返回北側(cè)方法指定位置的入?yún)ⅲ?br />   callRealMethod() 接口返回實際的調(diào)用方法;
  上面的例子已經(jīng)說明了 Mockito 能跟蹤被 Mock 對象所有的方法調(diào)用和它們的入?yún)。除了對方法調(diào)用結(jié)果是否正確的測試,有時還需要驗證一些方法的行為,比如驗證方法被調(diào)用的次數(shù),驗證方法的入?yún)⒌,Mockito 通過 verify() 方法實現(xiàn)這些場景的測試需求。這被稱為“行為測試”。
@Test
publicvoidtestVerify(){
Duplicator mock = mock(Duplicator.class);
when(mock.getUniqueId()).thenReturn(43);
mock.duplicate("Halo");
mock.getUniqueId();
mock.getUniqueId();
verify(mock).duplicate(Matchers.eq("Halo"));
verify(mock, times(2)).getUniqueId();
verify(mock, never()).someMethod();
verify(mock, atLeastOnce()).someMethod();
verify(mock, atLeast(2)).someMethod();
verify(mock, atMost(3)).someMethod();;
}
  verify() 內(nèi)的條件設(shè)置簡潔明了,第一個參數(shù)是 mock 對象,第二個參數(shù)可選,作為狀語描述,從方法的名稱上能知道具體的用法,不多贅述了。
  Mockito 支持通過 @Spy 注解或 spy() 方法包裹實際對象,除非明確指定對象,否則都會調(diào)用包裹后的對象。這種方式實現(xiàn)了對實際對象的部分自定義修改。
  @Test
  publicvoidtestSpy(){
  List<String> spyList = spy(new ArrayList<String>());
  assertEquals(0, spyList.size());
  doReturn(100).when(spyList).size();
  assertEquals(100, spyList.size());
  }
  上面的測試代碼中, spy() 修改了 ArrayList 對象的 size() 。但是如果只是在執(zhí)行某個操作是返回一個期望值,用之前的 mock() 也能實現(xiàn), spy() 存在的理由是什么,看下面的代碼能解釋二者之間的差異:
  @Test
  publicvoiddifferMockSpy(){
  List mock = mock(ArrayList.class);
  mock.add("one");
  verify(mock).add("one");
  assertEquals(0, mock.size());
  List spy = spy(new ArrayList());
  spy.add("one");
  verify(spy).add("one");
  assertEquals(1, spy.size());
  }
  從上面的運行結(jié)果可以看出, mock() 傳入的是類,創(chuàng)建出來的是一個裸的實例,只是為了跟蹤該實例下的方法調(diào)用,而不會對實例有其他副作用產(chǎn)生;而 spy() 傳入的是類實例,它會對該實例進(jìn)行包裹,創(chuàng)建出來的實例和源實例相同,的不同在于, spy() 包裹后的實例可以對實例內(nèi)部進(jìn)行自定義的改動。
  對于依賴注入,Mockito 支持通過 @InjectMocks 注解將被標(biāo)記的對象自動注入,其依賴會由 mock 出來的對象實例來填充。Mockito 會依次嘗試通過 constructor injection、 property injection 和 filed injection,注意,如果其中任一注入策略失敗,Mockito 也不會報告錯誤,必須自行解決依賴。
  Constructor injection : @InjectMocks 優(yōu)先選擇的注入策略,如果對象通過構(gòu)造函數(shù)成功 mock 出來,則不會再進(jìn)行后面的注入策略。
  Property setter injection :會首先根據(jù)屬性的類型(如果類型匹配則忽略變量名),如果有多個匹配項,則選擇 mock 名和屬性名相同的變量進(jìn)行注入。
  Field injection :同樣首先根據(jù)域的類型(如果類型匹配則忽略變量名),如果有多個匹配項,則選擇 mock 名和域名相同的變量進(jìn)行注入。
  參考下面的樣例代碼:
public classArticleManagerTestextendsSampleBaseTestCase{
@Mock
private ArticleCalculator calculator;
@Mock(name = "database")
private ArticleDatabase dbMock; // note the mock name attribute
@Spy
private UserProvider userProvider = new ConsumerUserProvider();
@InjectMocks
private ArticleManager manager;
@Test
publicvoidshouldDoSomething(){
manager.initiateArticle();
verify(database).addListener(any(ArticleListener.class));
}
}
public classSampleBaseTestCase{
@Before
publicvoidinitMocks(){
MockitoAnnotations.initMocks(this);
}
}
  上面代碼中, @InjectMocks 注解會把 mock 出來的 dbMock 和 calculator 注入進(jìn) manager 中。 ArticleManager 可以只有一個有參構(gòu)造函數(shù),或者只有無參構(gòu)造器,或者都有。需要注意的是,Mockito 無法實例化 inner class、local class、abstract class 和 interface。
  對需要注入的域,Constructor injection 會發(fā)生在下面的代碼中:
public classArticleManager{
ArticleManager(ArticleCalculator calculator, ArticleDatabase database) {
// parameterized constructor
}
}
Property setter injection 在下面的代碼中完成:
public classArticleManager{
// no-arg constructor
ArticleManager() {  }
// setter
voidsetDatabase(ArticleDatabase database){ }
// setter
voidsetCalculator(ArticleCalculator calculator){ }
}
Field injection:
public classArticleManager{
private ArticleDatabase database;
private ArticleCalculator calculator;
}

上一頁12下一頁
軟件測試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd