- Mockito 简介
Mockito是一个java仿冒架构,所谓的仿冒就是创建一个“虚拟”的java类来模拟一个java类的行为。之所以使用仿冒可能出于以下几个原因:
- 某个类已经进入单元测试,但是协作的类还没有开发完成;
- 协作类可能有缺陷,使用仿冒机制隔离缺陷;
- 模拟在单元测试阶段难以获得的对象,这些对象往往与环境和容器相关,如:HttpServletRequest
Mockito的三个事件,mock仿冒、验证(verification)和打桩(stubbing)。所谓的验证就是断言预期的行为,所谓的打桩就是mock对象在特定情况下的入参和返回
2. Maven 配置
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.kubek2k</groupId> <artifactId>springockito</artifactId> <version>1.0.9</version> <scope>test</scope> </dependency> <dependency> <groupId>org.kubek2k</groupId> <artifactId>springockito-annotations</artifactId> <version>1.0.9</version> <scope>test</scope> </dependency>
3.使用
// 引用的静态资源 import static org.mockito.Mockito.*; import static org.junit.Assert.*;
1.简单的例子演示验证和打桩
@Test public void test(){ //制造一个mock 类 Map<String, Object> map = mock( Map.class ); map.put( "key", "value" ); map.clear(); //验证mock类是否调用 verify( map ).put( "key", "value" );// 只进行equals匹配 //验证clear至少调用过一次 verify( map, atLeast( 1 ) ).clear(); //打桩,尚未进行打桩的参数返回null Assert.assertEquals( null, map.get( "any" ) ); // 进行打桩后 when( map.get( "Hello" ) ).thenReturn( "World" ); Assert.assertEquals( "World", map.get( "Hello" ) ); }
通过上面示例我们可以看出mock的没次调用我们都能进行监控,相应的mock可以准确的模拟对象的行为,忽略对象行为的实现细节。
mock未打桩时对象的返回值
- 返回Object的方法,mock默认返回null;
- 返回集合的方法,mock默认返回空;
- 返回数字的方法,mock默认返回0;
- 返回boolean的方法,mock默认返回false;
mock可以进行多次打桩,没次打桩都会覆盖前一个的值
参数匹配
@Test public void test(){ List list = mock( List.class ); //任何整数参数 when( list.get( anyInt() ) ).thenReturn( null ); //两个任意参数 when( list.addAll(anyInt(), anyCollection() ) ).thenReturn( false ); }mock除了支持任意整数和人与集合 还用 anyBoolean()、anyByte()、anyLong()等
参数捕获
@Test public void test(){ List list = mock( List.class ); ArgumentCaptor<Integer> argument = ArgumentCaptor.forClass( Integer.class ); list.add( 1 );//这个参数会被捕获 verify( list ).add( argument.capture() ); //执行捕获 //后续对参数进行进一步验证 Assert.assertEquals( new Integer( 1 ), argument.getValue() ); }
返回值设定
@Test public void test(){ List list = mock( List.class ); when( list.get( 0 ) ).thenReturn( 10 ); // 设置多个返回值,没次调用返回值不一样 when( list.get( 0 ) ).thenReturn( 1, 2, 3 ); //设置异常返回 when( list.get( 0 ) ).thenThrow( new RuntimeException() ); //依据入参决定返回值 when( list.get( anyInt() ) ).then( new Answer<Object>() { public Object answer( InvocationOnMock invocation ) throws Throwable { return "El-" + invocation.getArguments()[0]; } } ); //连续仿冒 when( list.get( 0 ) ) .thenReturn( 1 ) //第一次调用的返回值 .thenReturn( 2 )//第二次调用的返回值 .thenThrow( new RuntimeException() ); //以后调用的返回值 //设置未打桩方法的默认返回值 mock( List.class, new Answer<Object>() { @Override public Object answer( InvocationOnMock invocation ) throws Throwable { return null; } } ); }
验证调用次数
List list = mock( List.class ); //验证基于某个精确实参的调用发生的次数 verify( list, times( 1 ) ).add( 1 ); //一次 verify( list, times( 2 ) ).add( 2 ); //二次 verify( list, atLeastOnce() ).add( 1 ); //至少一次 verify( list, atLeast( 3 ) ).add( 3 ); //至少三次 verify( list, atMost( 3 ) ).add( 3 ); //至多三次 //参数匹配也适用于验证的时候: verify( list, atMost( 3 ) ).add( argThat( new ArgumentMatcher<Object>() { public boolean matches( Object argument ) { return false; } } ) ); //断言Mock上没有发生任何未验证的调用 verifyNoMoreInteractions( list );
调用真是方法
List<Integer> list = new ArrayList<Integer>(); List<Integer> spy = spy( list ); //对真实对象进行打桩 when( spy.size() ).thenReturn( 100 ); //另外一种打桩方式,防止异常 doReturn( 1 ).when( spy ).get( 0 ); //下面这种方式是不行的,因为未打桩前会调用真实方法,导致IndexOutOfBoundsException when( spy.get( 0 ) ).thenReturn( 1 ); spy.add( 1 );//调用真实的方法 spy.size();//调用仿冒的方法 verify( spy ).add( 1 ); //验证调用了一次
启动支持注解
//如果要启用注解支持,可以使用该运行类 //或者手工调用MockitoAnnotations.initMocks(this) @RunWith ( MockitoJUnitRunner.class ) public class MockitoTest { //自动创建Mock @Mock private List<Object> list; }
其他
List mock = mock(List.class); when(mock.size()).thenReturn(10); reset(mock); //重置仿冒 //单行调用即完成的Mock Car boringStubbedCar = when(mock(Car.class).shiftGear()).thenThrow(EngineNotStarted.class).getMock();