转载自:http://blog.csdn.net/jinnchang/article/details/8814720
1、简介
所谓的mock,就是指,如果我们写的代码依赖于某些对象,而这些对象又很难手动创建,那么就使用一个虚拟的对象来测试。Mockito是目前java中使用比较流行的mock工具。它使用起来简单,学习成本很低,而且具有非常简洁的Api,测试代码的可读性很高。因此它十分受欢迎,用户群越来越多,很多的开源的软件也选择了Mockito。
如果项目是通过Maven管理的,需要在项目的pom.xml中增加如下的依赖:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
2、展示一个简单完整的mockito应用实例
/**
* Description:被测试类
* UserService.java Create on 2013-4-17 下午4:47:17
* @author 张景
* @version 2.0
* Copyright (c) 2013 telek. All Rights Reserved.
*/
public class UserService {
private UserDao userDao;
public String getName(String userId) {
return userDao.getName(userId);
}
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
/**
* Description:对应的测试类
* UserServiceTest.java Create on 2013-4-17 下午4:48:34
* @author 张景
* @version 2.0
* Copyright (c) 2013 telek. All Rights Reserved.
*/
public class UserServiceTest {
@Test
public void testGetName() {
//创建Mock对象
UserDao userDao = Mockito.mock(UserDao.class);
//设置方法调用的预期返回
Mockito.when(userDao.getName("Tom")).thenReturn("Hello Tom");
//验证方法调用结果
UserService userService = new UserService();
userService.setUserDao(userDao);
String result = userService.getName("Tom");
assertEquals("Hello Tom", result);
//验证方法调用过程
Mockito.verify(userDao,Mockito.times(1)).getName("Tom");
}
}
3、了解Stub和Mock
在开始使用Mockito之前,先简单的了解一下Stub和Mock的区别:Stub对象用来提供测试时所需要的测试数据,可以对各种交互设置相应的回应。例如我们可以设置方法调用的返回值等等。Mockito中when(mock).thenReturn(value)这样的语法便是设置方法调用的返回值。另外也可以设置方法在何时调用会抛异常等。Mock对象用来验证测试中所依赖对象间的交互是否能够达到预期。Mockito中用verify(mock).someMethod()语法来验证someMethod方法是否按照预期进行了调用。
4、Mockito运行过程
A、Mock对象的创建
创建Mock对象的语法为:
mock(Class<T> classToMock)
mock(Class<T> classToMock, String name)
可以对类和接口进行mock对象的创建,创建的时候可以为mock对象命名,也可以忽略命名参数。为mock对象命名的好处就是调试的时候会很方便,比如,我们mock多个对象,在测试失败的信息中会把有问题的mock对象打印出来,有了名字我们可以很容易定位和辨认出是哪个mock对象出现的问题。另外它也有限制,对于final类、匿名类和Java的基本类型是无法进行mock的。
mock(Class<T> classToMock)
mock(Class<T> classToMock, String name)
可以对类和接口进行mock对象的创建,创建的时候可以为mock对象命名,也可以忽略命名参数。为mock对象命名的好处就是调试的时候会很方便,比如,我们mock多个对象,在测试失败的信息中会把有问题的mock对象打印出来,有了名字我们可以很容易定位和辨认出是哪个mock对象出现的问题。另外它也有限制,对于final类、匿名类和Java的基本类型是无法进行mock的。
B、Mock对象的期望行为及返回值设定
通过when(mock.someMethod()).thenReturn(value) 来设定mock对象某个方法调用时的返回值。(注意:Mockito对于static和final修饰的方法是无法进行设定的。)
a、对方法设定返回值:
方式1:when(mock.someMethod()).thenReturn(value);
方式2:doReturn((value)).when(mock).someMethod();
b、对方法设定返回异常
方式1:when(mock.someMethod()).thenThrow(new Exception());
方式2:doThrow(new Exception()).when(mock).someMethod();
c、对迭代风格设定返回值
方式1:when(mock.someMethod()).thenReturn(value1).thenReturn(value2);
方式2:when(mock.someMethod()).thenReturn(value1,value2);
d、对void方法进行方法预期设定
doNothing().when(mock).someMethod();
C、Mock对象的行为验证
mock对象一旦建立便会自动记录自己的交互行为,所以我们可以有选择的对它的交互行为进行验证。在Mockito中验证mock对象交互行为的方法是:verify(mock).someMethod()。
a、验证的基本方法:verify(mock).someMethod();
b、验证未曾执行的方法:verify(mock,never()).someMethod();
c、查询多余的方法调用:verifyNoMoreInteractions(mock);
verifyNoMoreInteractions()方法可以传入多个mock对象作为参数,用来验证传入的这些mock对象是否存在没有验证过的调用方法。
d、查询没有交互的mock对象
verifyZeroInteractions()也是一个测试工具,源码和verifyNoMoreInteractions()的实现是一样的,为了提高逻辑的可读性,所以只不过名字不同。
e、验证方法调用的次数:verify(mock,times(n));
如果要验证Mock对象的某个方法调用次数,则需给verify方法传入相关的验证参数,它的调用接口是verify(T mock,VerificationMode mode)。times(n)参数便是验证调用次数的参数,n代表方法调用次数。其实verify方法中如果不传调用次数的验证参数,它默认传入的便是times(1),即验证mock对象的方法是否只被调用一次,如果有多次调用测试方法将会失败。
Mockito除了提供times(n)方法供我们调用外,还提供了很多可选的方法:
never() ----没有被调用,相当于times(0)
atLeast(n) ----至少被调用n次
atLeastOnce() ----相当于atLeast(1)
atMost(n) ----最多被调用n次
Mockito除了提供times(n)方法供我们调用外,还提供了很多可选的方法:
never() ----没有被调用,相当于times(0)
atLeast(n) ----至少被调用n次
atLeastOnce() ----相当于atLeast(1)
atMost(n) ----最多被调用n次
f、超时验证:verify(mock,timeout(millis)).someMethod();
Mockito提供对超时的验证,但是目前不支持在下面提到的顺序验证中使用。进行超时验证和上述的次数验证一样,也要在verify中进行参数的传入,参数为timeout(int millis),timeout方法中输入的是毫秒值。
D、mock对象的重置
Mockito提供了reset(mock)方法,用来重置mock对象。当mock对象被重置后,它将回到刚创建完的状态,没有任何stubbing和方法调用。这个特性平时是很少用到的,因为我们大都为每个test方法创建mock,所以没有必要对它进行重置。官方提供这个特性的唯一目的是使得我们能在有容器注入的mock对象中工作更为方便。所以,当决定要使用这个方法的时候,首先应该考虑一下我们的测试代码是否简洁和专注,测试方法是否已经超长了。