mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。
stub是真是对象的一个模拟,比如调用者需要一个值,那就让stub输出一个值,如果调用者需要传递一个值给stub,那就在stub中定义一个方法接受该参数。但是这与mock的对象存在本质的区别:stub虽然说也是模拟,但其本质上对真是对象的一个简单实现,而无论它有多简单它都是一种实现,它是真是存在的,它里面包含了我们定义的操作代码;
@Controller
public class StubController {
@Autowired
StubService stubService;
@PostMapping()
public void Add(@RequestBody User user){
//when,当调用方法时返回一个期待值给finduser
User finduser =stubService.find(user);
}
}
@Service
public class StubServiceImpl implements StubService {
//@Autowired
//StubDao stubDao;
@Override
public User find(User user) {
User finduser = new User();
finduser.setId=user.getId();
finduser.setName=user.getName();
//User finduser = stubDao.select(user);
//Give,设置一个期待值返回
return finduser ;
}
}
Mock对象用来验证测试中所依赖的对象间的交互能否达到预期,mock的对象它根本是不存在的,哪怕一句的简单的不能再简单的代码都不存在。
@Test
public void when_thenReturn(){
//mock一个Iterator类
Iterator iterator = mock(Iterator.class);
//预设当iterator调用next()时第一次返回hello,第n次都返回world
when(iterator.next()).thenReturn("hello").thenReturn("world");
//使用mock的对象
String result = iterator.next() + " " + iterator.next() + " " + iterator.next();
//验证结果
assertEquals("hello world world",result);
}
目前支持Java语言的Mock测试工具有EasyMock、JMock、Mockito、MockCreator、Mockrunner、MockMaker等,Mockito是一个针对Java的Mocking框架。
它与EasyMock和JMock很相似,是一套通过简单的方法对于指定的接口或类生成 Mock 对象的类库,避免了手工编写Mock对象。但Mockito是通过在执行后校验什么已经被调用,它消除了对期望行为(Expectations)的需要。
使用Mockito,在准备阶段只需花费很少的时间,可以使用简洁的API编写出漂亮的测试,可以对具体的类创建Mock对象,并且有“监视”非Mock对象的能力。
一 mockito基本概念
Mock测试是单元测试的重要方法之一,而Mockito作为一个流行的Mock框架,简单易学,且有非常简洁的API,测试代码的可读性很高。
Mock测试就是在测试过程中,对于一些不容易构造(如HttpServletRequest必须在Servlet容器中才能构造出来)或者说获取比较复杂的对象(如JDBC中的ResultSet对象)或者说我们并不需要关注的对象,用一个虚拟的对象(Mock对象)来创建方便测试的测试方法。
Mock最大的功能是可以帮我们把单元测试的耦合分解开,如果代码中对另一个类或接口有依赖,它就能帮你模拟这些依赖,并帮你验证所调用的依赖的行为。
Java中目前主要的Mock测试工具有Mockito,JMock,EasyMock等等,很多Java Mock库如EasyMock或JMock都是expect-run-verify(期望-运行-测试)的方式,而Mockito则更简单:在执行后的互动中提问。使用Mockito主要记住,在执行前stub,而后在交互中验证即可。
- Mock和Stub都是通过一种更加快捷的方式让我们能够及时迅速的获取到我们期望的对象和数据。
- Stub更关注于状态,它可以通过硬编码一些输入或者输出,让我们获取我们需要的数据。
- Stub对象用来提供测试时所需要的测试数据,可以对各种交互设置相应的回应,比如说设置方法调用的返回值等等。我们在Mockito中可以通过when(…)thenReturn(…)来设置方法调用的返回值。
- Mock更关注于行为,它可以记录对象中各个方法都调用情况,如:是否被调用,调用了几次、在某种情况下是否会抛出异常等。
- Mock对象用来验证测试中所依赖的对象间的交互能否达到预期,我们可以在Mockito中用verify(…)methodXxx(…)语法来验证methodXxx方法是否按预期被调用。它也有限制,对于final类、匿名类和Java的基本类型是无法mock的。
如果项目是Maven管理的,那么就要在pom.xml中加入依赖:
org.mockito
mockito-all
1.8.5
test
然后在程序中直接import static org.mockito.Mockito.*; 即可
首先看一个例子:
@Test
public void argumentExpectTest(){
User userMock = mock(User.class);
Mockito.when(userMock.getName()).thenReturn("xiaoming");
System.out.println(userMock.getName());
// 关注参数有否传入
Mockito.verify(userMock).getName();
assertEquals("预期返回:xiaoming","xiaoming", userMock.getName());
// 关注调用的次数
Mockito.verify(userMock,times(2)).getName();
}
mockList模拟了User的对象,拥有User的所有方法和属性。when(…).thenReturn(…)是指当执行这个方法的时候,返回指定的值。相当于模拟配置对象的过程,为某些条件给定一个预期的返回值。
这里的User是User对象,并不是实现类,你也可以使用实现类,我们可以使用它们作为打桩对象。这里的打桩(Stub)也可以叫存根,就是把所需的测试数据塞进对象中,关注的是输入和输出。这里的when(…).thenReturn(…)就是在定义对象方法和参数(输入),然后在thenReturn()中指定结果(输出),这个过程就是Stub打桩,一旦这个方法被Stub了,就会一直返回这个stub的值。
@Test
public void argumentDoNothingTest(){
User userMock = mock(User.class);
doNothing().when(userMock).setName();
userMock.setName();
// 关注参数有否传入
Mockito.verify(userMock).setName();
// 关注调用的次数
Mockito.verify(userMock,times(1)).setName();
}
@Test
public void argumentDoThrowTest(){
User userMock = mock(User.class);
//二选一
Mockito.when(userMock.getName()).thenThrow(new RuntimeException());
doThrow(new RuntimeException()).when(userMock).getName();
userMock.getName();
}
thenThrow()和doThrow()两种方法,选其中一个使用即可。
@Test
public void argumentMatchersTest(){
Map mapMock = mock(Map.class);
Mockito.when(mapMock.put(anyInt(), anyString())).thenReturn("world");
mapMock.put(1, "hello");
//Mockito.verify(mapMock).put(anyInt(), eq("hello"));
Mockito.verify(mapMock).put(anyInt(), eq(anyString()));
}
Mockito.verify(mapMock).put(anyInt(), eq(“hello”));会报org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 异常,因为eq(“hello”)里面也需要参数匹配器的方法anyString()
之前的when(…).thenReturn(…)属于状态测试,有些时候,测试并不关心返回结果,而是关心方法是否被正确的参数调用过,这时候就应该使用验证方法了。从概念上讲,就是和状态测试不同的“行为测试”了。一旦通过mock对模拟对象打桩,意味着mockito会记录着这个模拟对象调用了什么方法,调用了多少次,最后由用户决定是否需要进行验证,即verify()方法。
userMock.setName("one");
userMock.setName("two");
//验证setName()方法是否被调用
verify(userMock ).setName("one");
// 关注参数有否传入
Mockito.verify(userMock).setName();
// 关注调用的次数
Mockito.verify(userMock,times(1)).setName();
Mockito 除了提供 times(N) 方法供我们调用外,还提供了很多可选的方法: never() 没有被调用,相当于 times(0)
atLeast(N) 至少被调用 N 次 atLeastOnce() 相当于 atLeast(1) atMost(N) 最多被调用 N 次
verify 也可以像 when 那样使用模拟参数,若方法中的某一个参数使用了matcher,则所有的参数都必须使用 matcher。
Mock对象是能调用stubbed方法,调用不了它真实的方法,但是Mockito可以监视一个真实的对象,这时对它进行方法调用时它将调用真实的方法,同时也可以stubbing这个对象的方法让它返回我们的期望值。同时,我们也可以用verify进行验证。
监视对象
监视一个对象需要调用spy(T object)方法,如:List spy = spy(new LinkedList());那么spy变量就在监视LinkedList实例。
被监视对象的Stubbing
stubbing被监视对象的方法时要慎用when(Object),如:
List spy = spy(new LinkedList());
//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");
//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
当调用when(spy.get(0)).thenReturn(“foo”)时,会调用真实对象的get(0),由于list是空的所以会抛出IndexOutOfBoundsException异常,用doReturn可以避免这种情况的发生,因为它不会去调用get(0)方法。
参考文章:
[1]: https://www.cnblogs.com/lyy-2016/p/6155445.html