Mockito介绍

2019/07/18

简单的介绍一下Mockito

简介

Mockito是一个基于Junit的mocking框架.何谓mocking框架呢,顾名思义就是”模仿”的意思,它可以模仿或半模仿(半模仿的意思就是说有一部分方法是模仿的有一部分不是)一个接口或类的所有非私有方法,因为是模仿,所以实际执行的方法(行为)就不一定是真正的方法体了,我们关注的只是出入参,当然了你也可以选择执行真正的方法,但是很多时候你会希望在测试方法里执行一段模仿正在方法的代码而在正式环境里才执行真正的代码,例如在执行一段测试方法是往往会和很多环境变量相关,如果环境变量中包含io操作,如果不去模仿io操作返回的结果而是去真正的执行io相关的代码,那将会是相当的慢,麻烦,以及不可控.

好,以上说的只是Mockito最为核心的特性,那么围绕”mock”这个特性扩展开来,它有一个暂存特性,利用这个特性可以mock指定具体入参的方法返回不同的结果.它还有一个可以校验mock方法是否得到正确执行且会报出明确校验错误的特性,包括某个入参的方法是否被执行过,执行了多少次等.而且它还有非常简洁易用的API使测试代码非常易读.Mockito遵循expect-run-verify语义,利用它简洁的API你只需要简单的几步就写出可读性高且会报出明确校验错误的测试代码了.

  1. 先mock出你所需要用到的对象以及对应的方法
  2. 执行你的测试代码,并获得期望结果
  3. 验证你的mock代码是否得到正确执行

来看看Mockito到底有多简单

先来看一个简单的例子

//用静态的方式引入Mockito
 import static org.mockito.Mockito.*;

 //创建一个mock实例
 List mockedList = mock(List.class);

 //使用mock实例
 mockedList.add("one");
 mockedList.clear();

 //验证mock实例是否执行过对应的方法
 verify(mockedList).add("one");
 verify(mockedList).clear();

看,一切就是这么简单

再来看一个暂存的列子

默认情况下,所有mock方法返回的都是null或者基数数据类型的默认值(例如int是0,boolean是false,等).但是经过暂存之后,mock方法就会返回你预设的值

 //创建一个mock实例
 LinkedList mockedList = mock(LinkedList.class);

 //暂存
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());

 //这里会打印 "first"
 System.out.println(mockedList.get(0));

 //这里会抛出一个 runtime exception
 System.out.println(mockedList.get(1));

 //这里会打印 "null" 因为 get(999) 没有被暂存
 System.out.println(mockedList.get(999));

 //验证暂存方法是否执行过
 verify(mockedList).get(0);
 verify(mockedList).get(1);

参数匹配器

 //暂存时使用内建的参数匹配器anyInt()
 when(mockedList.get(anyInt())).thenReturn("element");
 
 //你也可以把参数匹配器用在验证上
 verify(mockedList).get(anyInt());

验证mock方法调用次数

 mockedList.add("once");

 mockedList.add("twice");
 mockedList.add("twice");

 mockedList.add("three times");
 mockedList.add("three times");
 mockedList.add("three times");

 //这两种方式其实是一样的 - times(1) 是默认的
 verify(mockedList).add("once");
 verify(mockedList, times(1)).add("once");

 //校验这两个mock方法被调用了几次
 verify(mockedList, times(2)).add("twice");
 verify(mockedList, times(3)).add("three times");

 //用 never()校验. never() 其实就是 times(0)
 verify(mockedList, never()).add("never happened");

 //用 atLeast()/atMost() 校验,最少几次,最多几次
 verify(mockedList, atMostOnce()).add("once");
 verify(mockedList, atLeastOnce()).add("three times");
 verify(mockedList, atLeast(2)).add("three times");
 verify(mockedList, atMost(5)).add("three times");

另外一个特性spy

spy和mock不一样,spy在你调用没有暂存的方法时,会调用实例本身正在的方法体

List list = new LinkedList();
List spy = spy(list);

//你可以暂存返回值(眼尖的童鞋应该看出来了这里有点不一样,下面的例子会有说明)
doReturn(100).when(spy.size());

//这里是调用这种的方法体
spy.add("one");
spy.add("two");

//打印出 "one"
System.out.println(spy.get(0));

//size() 方法是被暂存过的所以打印出 100
System.out.println(spy.size());

spy要使用不同的方式去实现暂存

List list = new LinkedList();
List spy = spy(list);

//这里spy会调用了真正的方法,但是现在list是空的,所以会抛出IndexOutOfBoundsException
when(spy.get(0)).thenReturn("foo");

//你必须用doReturn()去实现暂存
doReturn("foo").when(spy).get(0);