import org.junit.Test; import org.mockito.Matchers; import org.mockito.Mockito; import java.util.List; import java.util.Map; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.*; /** * Created by MyWorld on 2016/1/26. */ public class MockitoDemo { @Test public void mockitoMapDemo1() { Map mockedMap = Mockito.mock(Map.class); when(mockedMap.get("city")).thenReturn("广州"); Object cityValue = mockedMap.get("city"); assertThat(cityValue.toString(), is("广州")); verify(mockedMap).get(Matchers.eq("city")); verify(mockedMap, times(2)); } @Test public void mockitoMapDemo2() { Map mockedMap = Mockito.mock(Map.class); // when(mockedMap.put(anyInt(), anyString())).thenReturn("world"); mockedMap.put(1, "hello"); verify(mockedMap).put(anyInt(), eq("hello")); } @Test public void mockitoListDemo() { List mockedList = Mockito.mock(List.class); mockedList.add("one"); mockedList.add("two"); verify(mockedList).add("one"); verify(mockedList, times(2)).add(anyString()); } }
JUnit 是单元测试框架。Mockito 与 JUnit 不一样,并非单元测试框架(这方面 JUnit 已经足够好了),它是用于生成模拟对象或者直接点说,就是”假对象“的工具。二者定位不一样,因此通常一般的作法就是联合 JUnit + Mockito 来进行测试。html
首先是配置 Mock 对象,看看例子怎么写的。java
List mockedList = Mockito.mock(List.class); when(mockedList.get(0)).thenReturn(1); assertEquals("Descriptive information ", 1, mockedList.get(0));
其中 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 的值。api
打桩须要注意如下几点:数组
mock 对象会覆盖整个被 mock 的对象,所以没有 stub 的方法只能返回默认值。又由于,咱们 mock 一个接口的时候,不少成员方法只是一个签名,并无实现,这就要咱们手动写出这些实现方法啦。典型地,咱们模拟一个 request 请求对象,你被测试的代码中使用了 HttpSerevletRequest 什么方法,就要写出相应的实现方法!框架
这里“打桩”以后,咱们执行 request.getParamter("foo") 就会返回 boo,若是不这样设定,Mockito 就会返回默认的 null,也不会报错说这个方法找不到。
mock 实例默认的会给全部的方法添加基本实现:返回 null 或空集合,或者 0 等基本类型的值。
这取决于方法返回类型,如 int 会返回 0,布尔值返回 false。对于其余 type 会返回 null。ide
打桩支持迭代风格的返回值设定,例如,工具
上述咱们一直在讨论被测试的方法都有返回值的,那么没有返回值的 void 方法呢?也是测试吗?答案是确定的。——只不过 Mockito 要求你的写法上有不一样,由于都没返回值了,调用 thenReturn(xxx) 确定不行,取而代之的写法是,学习
Mockito 还能对被测试的方法强行抛出异常,
如需指定异常类型,参见这里。
拿上面的例子说,其中一个问题,
这里 getParameter("foo") 这里咱们是写死参数 foo 的,可是若是我不关心输入的具体内容,能够吗?能够的,最好能像正则表达式那样,/w+ 表示任意字符串是否是很方便,不用考虑具体什么参数,只要是 字符串 型的参数,就能够打桩。如此方便的想法 Mockito 也考虑到了,提供 argument matchers 机制,例如 anyString() 匹配任何 String 参数,anyInt() 匹配任何 int 参数,anySet() 匹配任何 Set,any() 则意味着参数为任意值。例子以下,
when(mockedList.get(anyInt())).thenReturn("element");
System.out.println(mockedList.get(999));// 此时打印是 element
再进一步,自定义类型也能够,如 any(User.class),另,参见《学习 Mockito - 自定义参数匹配器》 和 这里 和 这里。
一个问题,thenReturn 是返回结果是咱们写死的。若是要让被测试的方法不写死,返回实际结果并让咱们能够获取到的——怎么作呢?
有时咱们须要自定义方法执行的返回结果,Answer 接口就是知足这样的需求而存在的。
例如模拟常见的 request.getAttribute(key),因为这原本是个接口,因此连内部实现都要本身写了。这次经过 Answer 接口获取参数内容。
利用 InvocationOnMock 提供的方法能够获取 mock 方法的调用信息。下面是它提供的方法:
void 方法能够获取参数,只是写法上有区别,
其实就是一个回调,——若是不是接口,是实现类的话,估计不用本身写实现。
前面提到的 when(……).thenReturn(……) 属于状态测试,某些时候,测试不关心返回结果,而是侧重方法有否被正确的参数调用过,这时候就应该使用 验证方法了。从概念上讲,就是和状态测试所不一样的“行为测试”了。
一旦使用 org.mockito.Mockito.mock() 对模拟对象打桩,意味着 Mockito 会记录着这个模拟对象调用了什么方法,还有调用了多少次。
最后由用户决定是否须要进行验证,即 org.mockito.Mockito.verify() 方法。
verify() 说明其做用的例子:
List mockedList = Mockito.mock(List.class); mockedList.add("one"); mockedList.add("two"); verify(mockedList).add("one");// 若是times不传入,则默认是1 verify(mockedList, times(2)).add(anyString());
Map mockedMap = Mockito.mock(Map.class); when(mockedMap.get("city")).thenReturn("广州"); Object cityValue = mockedMap.get("city"); assertThat(cityValue.toString(), is("广州")); // 关注参数有否传入 verify(mockedMap).get(Matchers.eq("city")); // 关注调用的次数 verify(mockedMap, times(2));
也就是说,这是对历史记录做一种回溯校验的处理。
这里补充一个学究的问题,所谓 Mock 与 Stub 打桩,其实它们之间不能互为其表。但 Mockito 语境中则 Stub 和 Mock 对象同时使用的。由于它既能够设置方法调用返回值,又能够验证方法的调用。有关 stub 和 mock 的详细论述请见 Martin Fowler 大叔的文章《Mocks Aren't Stub》。
Mockito 除了提供 times(N) 方法供咱们调用外,还提供了不少可选的方法:
verify 也能够像 when 那样使用模拟参数,若方法中的某一个参数使用了matcher,则全部的参数都必须使用 matcher。
Map mockedMap = Mockito.mock(Map.class); mockedMap.put("", ""); String newValue = "newValue"; String oldValue = "oldValue"; //若方法中的某一个参数使用了matcher,则全部的参数都必须使用 matcher. // 不然会报异常:org.mockito.exceptions.misusing.InvalidUseOfMatchersException: // When using matchers, all arguments have to be provided by matchers. when(mockedMap.put(anyInt(), eq(newValue))).thenReturn(oldValue);//mock a put operation Object oldValueForPut = mockedMap.put(1, newValue);//get the mock value assertThat(oldValueForPut.toString(), is(oldValue));//assert the mock operation verify(mockedMap).put(anyInt(), eq(newValue));//verify whether the mock operation execute or not mockedMap.put(1, "hello"); verify(mockedMap).put(anyInt(), eq("hello"));
其余高级用法,详见《学习 Mockito - Mock对象的行为验证》,主要特性以下,
参见《用mockito的verify来验证mock的方法是否被调用》:
看mockito的api时,一直都不清楚veriry()这个方法的做用,由于若是我mock了某个方法,确定是为了调用的啊。直到今天在回归接口测试用例的时候,发现有两个用例,用例2比用例1多了一个 mock 的步骤,不过最后的结果输出是同样的。因为代码作了修改,我从新 mock 后,其实用例2中对于的步骤是不会执行的,可测试仍是经过了。仔细查看后,发现mock的方法没有被调用,因此用例2和用例1就变成同样的了。因而,就产生了这么个需求:单单经过结果来判断正确与否仍是不够的,我还要判断是否按我指定的路径执行的用例。到这里,终于领略到了mockito的verify的强大威力,如下是示例代码:
若调用成功,则程序正常运行,反之则会报告: Wanted but not invoked:verify(mockedList).add("one"); 错误。
感受 verify 会用的比较少。
spy 的意思是你能够修改某个真实对象的某些方法的行为特征,而不改变他的基本行为特征,这种策略的使用跟 AOP 有点相似。下面举官方的例子来讲明:
能够看到 spy 保留了 list 的大部分功能,只是将它的 size() 方法改写了。不过 spy 在使用的时候有不少地方须要注意,一不当心就会致使问题,因此不到万不得已仍是不要用 spy。
出处在这里。
若是没有 JUnit,可使用 Mockito 的 @Before 的注解,进行一些前期的初始化工做,
若是有 JUnit,则无需 @Before,但要修改 JUnit 默认容器,
在 JUnit 中有不少个 Runner ,他们负责调用你的测试代码,每个 Runner 都有各自的特殊功能,你要根据须要选择不一样的 Runner 来运行你的测试代码。
----------------------------------------------------
貌似 Mockito 的注解都比较强大,有待之后再看看:
《学习Mockito - Mockito对Annotation的支持》, http://jilen.iteye.com/blog/1427368
参见资源:
1自动生成Mock类
在须要Mock的属性上标记@Mock注解,而后@RunWith(MockitoJUnitRunner.class)或者在setUp()方法中显示调用MockitoAnnotations.initMocks(this);生成Mock类便可。
3 Mock方法定制不再用录制、播放了
Mockito的Mock方法定制可读性很强,并且也不须要像EasyMock那样录制播放,定制后就可使用。
例如:
when(userDao.selectAll()).
thenReturn(Collections.<UserDomain>emptyList());
http://blog.csdn.net/dc_726/article/details/8568537