Mockito簡介
Mockito是一個單元測試框架,需要Junit的支持。在我們的項目中,都存在相當多的依賴關系,當我們在測試某一個業務相關的接口或則方法時,絕大多數時候是沒有辦法或則很難去添加所有的依賴,因為這中間肯定會涉及到別的業務邏輯。而在開發過程中,可能這個模塊根本都還沒有。那可咋怎啊?這個時候一種叫做mock測試的方式就順勢崛起。通過模擬出依賴對象,並對涉及到的方法設置預期值。這樣你就可以只關心依賴方法的結果,從而完成對本模塊的單元測試。這種方法還細化了測試粒度。棒棒的。想做更多了解就自行解決了。
Mockito的使用
- 首先來一個最基本的Junit測試
@Test public void stringUtilTest(){ boolean b = StringUtil.isEmpty("good"); Assert.assertTrue("must true",b);//斷言 }
像這種對工具類的測試,一般很少依賴別的類,所以直接斷言之。當然斷言的類型還有很多,這里就試用一下對boolean的斷言。
2. mockito對依賴的模擬,並設置預期返回值。針對依賴的方法有返回值。在單元測試時,我們不想也最好不要直接調用依賴的方法的具體實現,因為所依賴的方法可能本來就沒有經過測試,還存在bug,難道這時候又要為依賴的方法再寫一個test case?或則這個方法由別人開發,但是目前還沒有實現,難道要自己去實現?you`d better say NO!看看下面怎么做的。
import org.mockito.Mockito; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @auther guozg */ public class MockTest { @Test public void mockReturnTest(){ // mock creation 創建模擬對象 UserDao mockeDao = Mockito.mock(UserDao.class); UserService s = new UserService();//創建被測試類 s.setDao(mockeDao);//為被測試類添加依賴 Mockito.when(mockeDao.getData(Mockito.anyString())).thenReturn(4).thenReturn(1);//為模擬對象方法設置預期返回值, boolean b = s.checkDate(); //多個thenReturn表示多次調用時,依次返回 boolean b1 = s.checkDate(); //如果設置的預期個數少於調用次數,超過的調用都返回最后一個。 boolean b2 = s.checkDate(); //如果設置的預期個數多於調用次數,任然依次返回相應值 Mockito.verify(mockeDao, Mockito.times(1)).getData(Mockito.anyString()); Assert.assertTrue("must true",b); Assert.assertFalse("must true",b1); Assert.assertFalse("must true",b2); } } class UserService{ UserDao dao ; public boolean checkDate(){
String id = "123"; if(dao.getData(id)>3){ return true; } return false; } public UserDao getDao() { return dao; } public void setDao(UserDao dao) { this.dao = dao; } } class UserDao{ public Integer getData(String id){ return 0; }
}
這里隨便舉的簡單例子,我需要對UserService.checkDate()做一個單元測試。而這時候UserService對UserDao存在依賴關系。所以這時候為了隔離UserDao的實現,通過Mockito.mock模擬出UserDao對象。並為掉用的getData()設置了預期返回值。然后調用要測試的方法、驗證設置預期的方法是否被調用,最后對測試方法的返回值斷言。這里指的一說的是Mockito.anyString()這個方法,他的主要目的是表示在模擬狀態下,getData()的參數可以是任意字符串。當然也可以直接給定一些參數,如果模擬對象指定的參數和實際邏輯給的參數不一致,這時候不會返回實際UserDao中getData()的值也不會返回設置的預期值,而是返回的返回類型的默認值,比如int就返回0,boolean返回false等。Mockito還有很多類似的方法,比如anyInt(),anyBoolen(),anyCollection()等等。
還有就是看到thenReturn()方法,這是在為依賴方法設置預期返回值,這個就可以有自己控制了。然后看到可以連續多次設置,這個在代碼里面有注釋了,由於對語言表達能力的不自信,來個表格展示一下。
然后還有一個驗證方法是否被調用Mockito.verify()這個方法的參數是模擬的對象、VerificationMode。這個VerificationMode就是一個驗證模型,可以是代表調用次數、是否調用、超時驗證,等等(有些我也不知道干啥的,要進一步研究)。verify()可以理解為斷言。
3. 對於有返回值的方法,我們可以通過設置預期來控制,並且隔離掉具體的實現。但是對於沒有返回值得方法呢?不着急,Mockti為我們提供了另一種方式可以控制模擬對象的方法的行為,包括邏輯處理、拋異常、返回值、執行原方法邏輯以及什么都不做。doReturn()|doThrow()| doAnswer()|doNothing()|doCallRealMethod();下面先來測試一下doAnswer();首先,我們會在UserDao 中加一個setUser(User u)方法,為user設置年齡,然后通過UserService的checkData中調用。然后我們通過doAnwser去控制setUser的邏輯。
package com.centnet.train.user.controller; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * @auther guozg */ public class MockTest { @Test public void doAnwserTest(){ // mock creation 創建模擬對象 UserDao mockeDao = Mockito.mock(UserDao.class); UserService s = new UserService();//創建被測試類 User u = new User(); s.setDao(mockeDao);//為被測試類添加依賴 Mockito.doAnswer(new Answer() { public Object answer(InvocationOnMock invocationOnMock) throws Throwable { User u = invocationOnMock.getArgument(0); u.setName("ggg"); return null; } }).when(mockeDao).setUser(u); Mockito.when(mockeDao.getData()).thenReturn(4); boolean b = s.checkDate(u); Mockito.verify(mockeDao, Mockito.times(1)).getData(); Mockito.verify(mockeDao, Mockito.times(1)).setUser(u); Assert.assertTrue("must true",b); Assert.assertNotNull("控制不成功",u.getName());
Assert.assertNull("原邏輯被執行",u.getAge()); } } class UserService{ UserDao dao ; public boolean checkDate(User u){ dao.setUser(u); if(dao.getData()>3){ return true; } return false; } public UserDao getDao() { return dao; } public void setDao(UserDao dao) { this.dao = dao; } public void setUser(User user){ user.setAge(12); } } class UserDao{ public Integer getData(){ return 0; } public void setUser(User user){ user.setAge(12); } } class User{ String name; Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
我們在UserDao中只設置了age,但是對age為空的斷言卻成功了。這表示原邏輯沒有執行。再看看對Name的不為空斷言,也通過了。這表示對setUser的行為被doAnwser()控制了。doAnwser()需要傳入對一個Anwser對象,這個對象有點和代理對象類似。如果調用方法有返回值,則anwer()的返回值就是它。invocationOnMock.getArgument(0)獲取參數,然后就可以對參數操作啦啦啦啦。其他幾個也可以試試,比如改成調用原邏輯,Mockito.doCallRealMethod().when(mockeDao).setUser(u)。哎呀呀,這時候user的age就有了而且還是18.重點:對於在測試;類中的方法不用設置預期,會調用原邏輯,但是對於依賴類的方法就要設置預期了,否則會對有返回值的僅返回類型默認值、無返回值的直接啥都不做走人。
好了,下班了,先到這里,以后有新的接觸,在添加進來,以上例子略顯粗獷,如不慎入坑,請包涵!(說得好像有人會看似的!!!要真有人看,基於以上例子可靈活處理。)