摘自: http://blog.csdn.net/zhangxin09/article/details/42422643
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[-]
入门
模拟传入的参数 argument matchers
获取返回的结果
验证 Verify
Spy
总结例子
其他高级话题
JUnit 是单元测试框架。Mockito 与 JUnit 不同,并不是单元测试框架(这方面 JUnit 已经足够好了),它是用于生成模拟对象或者直接点说,就是”假对象“的工具。两者定位不同,所以一般通常的做法就是联合 JUnit + Mockito 来进行测试。
入门
首先是配置 Mock 对象,看看例子怎么写的。
[java] view plain copy
List mock = mock( List.class );
when( mock.get(0) ).thenReturn( 1 );
assertEquals( "预期返回1", 1, mock.get( 0 ) );// mock.get(0) 返回 1
其中 mock 是模拟 List 的对象,拥有 List 的所有方法和属性。when(xxxx).thenReturn(yyyy); 是指定当执行了这个方法的时候,返回 thenReturn 的值,相当于是对模拟对象的配置过程,为某些条件给定一个预期的返回值。相信通过这个简单的例子你可以明白所谓 Mock 便是这么一回事。
我们看到 List 为 Java.util.List 是接口,并不是实现类,但这不妨碍我们使用它作为我们的“打桩”对象,——当然你也可以使用实现类,传入 mock(obj) 方法中。这里提到的是"打桩(Stub,也有人称其为“存根”)"的概念,是一个形象的说法,就是把所需的测试数据塞进对象中,适用于基于状态的(state-based)测试,关注的是输入和输出。Mockito 中 when(…).thenReturn(…) 这样的语法来定义对象方法和参数(输入),然后在 thenReturn 中指定结果(输出)。此过程称为 Stub 打桩。一旦这个方法被 stub 了,就会一直返回这个 stub 的值。
打桩需要注意以下几点:
对于 static 和 final 方法, Mockito 无法对其 when(…).thenReturn(…) 操作。
当我们连续两次为同一个方法使用 stub 的时候,他只会只用最新的一次。
mock 对象会覆盖整个被 mock 的对象,因此没有 stub 的方法只能返回默认值。又因为,我们 mock 一个接口的时候,很多成员方法只是一个签名,并没有实现,这就要我们手动写出这些实现方法啦。典型地,我们模拟一个 request 请求对象,你被测试的代码中使用了 HttpSerevletRequest 什么方法,就要写出相应的实现方法!
[java] view plain copy
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getParameter("foo")).thenReturn("boo");
这里“打桩”之后,我们执行 request.getParamter("foo") 就会返回 boo,如果不这样设定,Mockito 就会返回默认的 null,也不会报错说这个方法找不到。mock 实例默认的会给所有的方法添加基本实现:返回 null 或空集合,或者 0 等基本类型的值。这取决于方法返回类型,如 int 会返回 0,布尔值返回 false。对于其他 type 会返回 null。
打桩支持迭代风格的返回值设定,例如,
[java] view plain copy
// 第一种方式
when(i.next()).thenReturn("Hello").thenReturn("World");
// 第二种方式
when(i.next()).thenReturn("Hello", "World");
// 第三种方式,都是等价的
when(i.next()).thenReturn("Hello");
when(i.next()).thenReturn("World");
第一次调用 i.next() 将返回 ”Hello”,第二次的调用会返回 ”World”。
上述我们一直在讨论被测试的方法都有返回值的,那么没有返回值的 void 方法呢?也是测试吗?答案是肯定的。——只不过 Mockito 要求你的写法上有不同,因为都没返回值了,调用 thenReturn(xxx) 肯定不行,取而代之的写法是,
[java] view plain copy
doNothing().when(obj).notify();
// 或直接
when(obj).notify();
Mockito 还能对被测试的方法强行抛出异常,
[java] view plain copy
when(i.next()).thenThrow(new RuntimeException());
doThrow(new RuntimeException()).when(i).remove(); // void 方法的
// 迭代风格
doNothing().doThrow(new RuntimeException()).when(i).remove(); // 第一次调用 remove 方法什么都不做,第二次调用抛出 RuntimeException 异常。
如需指定异常类型,参见这里。
模拟传入的参数 argument matchers
拿上面的例子说,其中一个问题,
[java] view plain copy
when(request.getParameter("foo")).thenReturn("boo");
这里 getParameter("foo") 这里我们是写死参数 foo 的,但是如果我不关心输入的具体内容,可以吗?可以的,最好能像正则表达式那样,/w+ 表示任意字符串是不是很方便,不用考虑具体什么参数,只要是 字符串 型的参数,就可以打桩。如此方便的想法 Mockito 也考虑到了,提供 argument matchers 机制,例如 anyString() 匹配任何 String 参数,anyInt() 匹配任何 int 参数,anySet() 匹配任何 Set,any() 则意味着参数为任意值。例子如下,
[java] view plain copy 在CODE上查看代码片派生到我的代码片
when(mockedList.get(anyInt())).thenReturn("element");
System.out.println(mockedList.get(999));// 此时打印是 element
再进一步,自定义类型也可以,如 any(User.class),另,参见《学习 Mockito - 自定义参数匹配器》 和 这里 和 这里。
获取返回的结果
一个问题,thenReturn 是返回结果是我们写死的。如果要让被测试的方法不写死,返回实际结果并让我们可以获取到的——怎么做呢?有时我们需要自定义方法执行的返回结果,Answer 接口就是满足这样的需求而存在的。
例如模拟常见的 request.getAttribute(key),由于这本来是个接口,所以连内部实现都要自己写了。此次通过 Answer 接口获取参数内容。
[java] view plain copy
final Map<String, Object> hash = new HashMap<String, Object>();
Answer aswser = new Answer() {
public String answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
return hash.get(args[0].toString()).toString();
}
};
when(request.getAttribute("isRawOutput")).thenReturn(true);
when(request.getAttribute("errMsg")).thenAnswer(aswser);
when(request.getAttribute("msg")).thenAnswer(aswser);
利用 InvocationOnMock 提供的方法可以获取 mock 方法的调用信息。下面是它提供的方法:
getArguments() 调用后会以 Object 数组的方式返回 mock 方法调用的参数。
getMethod() 返回 java.lang.reflect.Method 对象
getMock() 返回 mock 对象
callRealMethod() 真实方法调用,如果 mock 的是接口它将会抛出异常
void 方法可以获取参数,只是写法上有区别,
[java] view plain copy
doAnswer(new Answer