Mockito简介
Mockito是一个单元测试框架,需要Junit的支持。在我们的项目中,都存在相当多的依赖关系,当我们在测试某一个业务相关的接口或则方法时,绝大多数时候是没有办法或则很难去添加所有的依赖,因为这中间肯定会涉及到别的业务逻辑。而在开发过程中,可能这个模块根本都还没有。那可咋怎啊?这个时候一种叫做mock测试的方式就顺势崛起。通过模拟出依赖对象,并对涉及到的方法设置预期值。这样你就可以只关心依赖方法的结果,从而完成对本模块的单元测试。这种方法还细化了测试粒度。棒棒的。想做更多了解就自行解决了。
Mockito的使用
-
首先来一个最基本的Junit测试
@Test public void stringUtilTest(){
1 boolean b = StringUtil.isEmpty("good"); 2 Assert.assertTrue("must true",b);//断言 3 4}
像这种对工具类的测试,一般很少依赖别的类,所以直接断言之。当然断言的类型还有很多,这里就试用一下对boolean的断言。
2. mockito对依赖的模拟,并设置预期返回值。针对依赖的方法有返回值。在单元测试时,我们不想也最好不要直接调用依赖的方法的具体实现,因为所依赖的方法可能本来就没有经过测试,还存在bug,难道这时候又要为依赖的方法再写一个test case?或则这个方法由别人开发,但是目前还没有实现,难道要自己去实现?you`d better say NO!看看下面怎么做的。
1import org.mockito.Mockito; 2 3import java.util.ArrayList; 4import java.util.Date; 5import java.util.List; 6 7/** 8 * @auther guozg 9 */ 10 11public class MockTest { 12 @Test 13 public void mockReturnTest(){ 14 // mock creation 创建模拟对象 15 UserDao mockeDao = Mockito.mock(UserDao.class); 16 UserService s = new UserService();//创建被测试类 17 s.setDao(mockeDao);//为被测试类添加依赖 18 Mockito.when(mockeDao.getData(Mockito.anyString())).thenReturn(4).thenReturn(1);//为模拟对象方法设置预期返回值, 19 boolean b = s.checkDate(); //多个thenReturn表示多次调用时,依次返回 20 boolean b1 = s.checkDate(); //如果设置的预期个数少于调用次数,超过的调用都返回最后一个。 21 boolean b2 = s.checkDate(); //如果设置的预期个数多于调用次数,任然依次返回相应值 22 Mockito.verify(mockeDao, Mockito.times(1)).getData(Mockito.anyString()); 23 Assert.assertTrue("must true",b); 24 Assert.assertFalse("must true",b1); 25 Assert.assertFalse("must true",b2); 26 } 27} 28 29class UserService{ 30 UserDao dao ; 31 public boolean checkDate(){ String id = "123"; 32 if(dao.getData(id)>3){ 33 return true; 34 } 35 return false; 36 } 37 38 public UserDao getDao() { 39 return dao; 40 } 41 42 public void setDao(UserDao dao) { 43 this.dao = dao; 44 } 45 46} 47 48class UserDao{ 49 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的逻辑。
1package com.centnet.train.user.controller; 2 3import org.junit.Assert; 4import org.junit.Test; 5import org.junit.runner.RunWith; 6import org.mockito.Mockito; 7import org.mockito.invocation.InvocationOnMock; 8import org.mockito.stubbing.Answer; 9 10import java.util.ArrayList; 11import java.util.Date; 12import java.util.List; 13 14/** 15 * @auther guozg 16 */ 17 18public class MockTest { 19 20 21 @Test 22 public void doAnwserTest(){ 23 // mock creation 创建模拟对象 24 UserDao mockeDao = Mockito.mock(UserDao.class); 25 UserService s = new UserService();//创建被测试类 26 User u = new User(); 27 s.setDao(mockeDao);//为被测试类添加依赖 28 Mockito.doAnswer(new Answer() { 29 public Object answer(InvocationOnMock invocationOnMock) throws Throwable { 30 User u = invocationOnMock.getArgument(0); 31 u.setName("ggg"); 32 return null; 33 } 34 }).when(mockeDao).setUser(u); 35 Mockito.when(mockeDao.getData()).thenReturn(4); 36 boolean b = s.checkDate(u); 37 38 Mockito.verify(mockeDao, Mockito.times(1)).getData(); 39 Mockito.verify(mockeDao, Mockito.times(1)).setUser(u); 40 Assert.assertTrue("must true",b); 41 Assert.assertNotNull("控制不成功",u.getName()); Assert.assertNull("原逻辑被执行",u.getAge()); 42 } 43} 44 45class UserService{ 46 UserDao dao ; 47 public boolean checkDate(User u){ 48 dao.setUser(u); 49 if(dao.getData()>3){ 50 return true; 51 } 52 return false; 53 } 54 55 public UserDao getDao() { 56 return dao; 57 } 58 59 public void setDao(UserDao dao) { 60 this.dao = dao; 61 } 62 63 public void setUser(User user){ 64 user.setAge(12); 65 66 } 67 68} 69 70class UserDao{ 71 public Integer getData(){ 72 return 0; 73 } 74 75 public void setUser(User user){ 76 user.setAge(12); 77 } 78} 79 80class User{ 81 82 String name; 83 Integer age; 84 85 public String getName() { 86 return name; 87 } 88 89 public void setName(String name) { 90 this.name = name; 91 } 92 93 public Integer getAge() { 94 return age; 95 } 96 97 public void setAge(Integer age) { 98 this.age = age; 99 } 100}
我们在UserDao中只设置了age,但是对age为空的断言却成功了。这表示原逻辑没有执行。再看看对Name的不为空断言,也通过了。这表示对setUser的行为被doAnwser()控制了。doAnwser()需要传入对一个Anwser对象,这个对象有点和代理对象类似。如果调用方法有返回值,则anwer()的返回值就是它。invocationOnMock.getArgument(0)获取参数,然后就可以对参数操作啦啦啦啦。其他几个也可以试试,比如改成调用原逻辑,Mockito.doCallRealMethod().when(mockeDao).setUser(u)。哎呀呀,这时候user的age就有了而且还是18.重点:对于在测试;类中的方法不用设置预期,会调用原逻辑,但是对于依赖类的方法就要设置预期了,否则会对有返回值的仅返回类型默认值、无返回值的直接啥都不做走人。
好了,下班了,先到这里,以后有新的接触,在添加进来,以上例子略显粗犷,如不慎入坑,请包涵!(说得好像有人会看似的!!!要真有人看,基于以上例子可灵活处理。)