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

  再靜態(tài)導(dǎo)入 org.mockito.Mockito.*; 里的靜態(tài)方法,這樣能在測(cè)試方法進(jìn)行對(duì)象的 Mock。Mockito 支持通過(guò)靜態(tài)方法 mock() 來(lái) Mock 對(duì)象,或者通過(guò) @Mock 注解,來(lái)創(chuàng)建 Mock 對(duì)象,但必須將其實(shí)例化。先演示下如何 Mock 對(duì)象:
  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 出來(lái)的對(duì)象擁有和源對(duì)象同樣的方法和屬性, when() 和 thenReturn() 方法是對(duì)源對(duì)象的配置,怎么理解,是說(shuō)在第一步 mock() 時(shí),mock 出來(lái)的對(duì)象還不具備被 Mock 對(duì)象實(shí)例的行為特征,而 when(...).thenReturn(...) 是根據(jù)條件去配置源對(duì)象的預(yù)期行為,即:當(dāng)執(zhí)行 when() 中的操作時(shí),返回 thenReturn() 中的結(jié)果。比如上面的代碼中,mock 出來(lái)的 i 實(shí)例在被遍歷時(shí)會(huì)依次輸出 “hello” 和 “world”, assertEquals() 是對(duì)預(yù)期結(jié)果和實(shí)際結(jié)果的判斷。
  同理,也可以 Mock 網(wǎng)絡(luò)請(qǐng)求,比如 HttpServletRequest 里的參數(shù),也可以通過(guò)上面的方式來(lái)設(shè)定被 Mock 的源對(duì)象的表現(xiàn)行為。
  對(duì)于 when() 不定條件,Mockito 定義了 any() 、 anyInt() 、 anyString() 、 anySet() 等方法來(lái)匹配指定類型的不定輸入, anyInt() 匹配 int 參數(shù), anyString() 匹配 String 參數(shù), any() 匹配 任意類型的參數(shù)。如果需要匹配自定義的類型,可以通過(guò) any(CustomedClass.class) 來(lái)配置。
  thenReturn() 返回的是一個(gè)確定值,這在模擬可見(jiàn)的行為時(shí)是沒(méi)問(wèn)題的,但有時(shí)候,我們需要得到一個(gè)復(fù)雜的不定輸出的行為,比如返回一個(gè)回調(diào)方法,或者返回一個(gè)類實(shí)例,Mockito 可以通過(guò) thenAnswer() 來(lái)實(shí)現(xiàn)。參考 StackOverflow 上的這篇問(wèn)答 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 接口提供了獲取被測(cè)試方法的調(diào)用信息的幾個(gè)重要方法:
  getMock() 接口返回 mock 對(duì)象;
  getMethod() 接口返回被調(diào)用方法的 Method 對(duì)象;
  getArguments() 接口返回被測(cè)試方法的入?yún)⒘斜恚?br />   getArgument() 接口返回北側(cè)方法指定位置的入?yún)ⅲ?br />   callRealMethod() 接口返回實(shí)際的調(diào)用方法;
  上面的例子已經(jīng)說(shuō)明了 Mockito 能跟蹤被 Mock 對(duì)象所有的方法調(diào)用和它們的入?yún)ⅰ3藢?duì)方法調(diào)用結(jié)果是否正確的測(cè)試,有時(shí)還需要驗(yàn)證一些方法的行為,比如驗(yàn)證方法被調(diào)用的次數(shù),驗(yàn)證方法的入?yún)⒌,Mockito 通過(guò) verify() 方法實(shí)現(xiàn)這些場(chǎng)景的測(cè)試需求。這被稱為“行為測(cè)試”。
@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è)置簡(jiǎn)潔明了,第一個(gè)參數(shù)是 mock 對(duì)象,第二個(gè)參數(shù)可選,作為狀語(yǔ)描述,從方法的名稱上能知道具體的用法,不多贅述了。
  Mockito 支持通過(guò) @Spy 注解或 spy() 方法包裹實(shí)際對(duì)象,除非明確指定對(duì)象,否則都會(huì)調(diào)用包裹后的對(duì)象。這種方式實(shí)現(xiàn)了對(duì)實(shí)際對(duì)象的部分自定義修改。
  @Test
  publicvoidtestSpy(){
  List<String> spyList = spy(new ArrayList<String>());
  assertEquals(0, spyList.size());
  doReturn(100).when(spyList).size();
  assertEquals(100, spyList.size());
  }
  上面的測(cè)試代碼中, spy() 修改了 ArrayList 對(duì)象的 size() 。但是如果只是在執(zhí)行某個(gè)操作是返回一個(gè)期望值,用之前的 mock() 也能實(shí)現(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());
  }
  從上面的運(yùn)行結(jié)果可以看出, mock() 傳入的是類,創(chuàng)建出來(lái)的是一個(gè)裸的實(shí)例,只是為了跟蹤該實(shí)例下的方法調(diào)用,而不會(huì)對(duì)實(shí)例有其他副作用產(chǎn)生;而 spy() 傳入的是類實(shí)例,它會(huì)對(duì)該實(shí)例進(jìn)行包裹,創(chuàng)建出來(lái)的實(shí)例和源實(shí)例相同,的不同在于, spy() 包裹后的實(shí)例可以對(duì)實(shí)例內(nèi)部進(jìn)行自定義的改動(dòng)。
  對(duì)于依賴注入,Mockito 支持通過(guò) @InjectMocks 注解將被標(biāo)記的對(duì)象自動(dòng)注入,其依賴會(huì)由 mock 出來(lái)的對(duì)象實(shí)例來(lái)填充。Mockito 會(huì)依次嘗試通過(guò) constructor injection、 property injection 和 filed injection,注意,如果其中任一注入策略失敗,Mockito 也不會(huì)報(bào)告錯(cuò)誤,必須自行解決依賴。
  Constructor injection : @InjectMocks 優(yōu)先選擇的注入策略,如果對(duì)象通過(guò)構(gòu)造函數(shù)成功 mock 出來(lái),則不會(huì)再進(jìn)行后面的注入策略。
  Property setter injection :會(huì)首先根據(jù)屬性的類型(如果類型匹配則忽略變量名),如果有多個(gè)匹配項(xiàng),則選擇 mock 名和屬性名相同的變量進(jìn)行注入。
  Field injection :同樣首先根據(jù)域的類型(如果類型匹配則忽略變量名),如果有多個(gè)匹配項(xiàng),則選擇 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 注解會(huì)把 mock 出來(lái)的 dbMock 和 calculator 注入進(jìn) manager 中。 ArticleManager 可以只有一個(gè)有參構(gòu)造函數(shù),或者只有無(wú)參構(gòu)造器,或者都有。需要注意的是,Mockito 無(wú)法實(shí)例化 inner class、local class、abstract class 和 interface。
  對(duì)需要注入的域,Constructor injection 會(huì)發(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;
}

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