mockito框架
2016-04-09 15:56:26
参考自
http://www.cnblogs.com/silence-hust/p/5017233.html
http://blog.csdn.net/sdyy321/article/details/38757135
首先创建一个maven工程
在pom文件中,存在如下依赖
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.9.5</version> <scope>test</scope> </dependency> </dependencies>
1、验证行为是否发生
@Test public void mockedList(){ List mockedList = mock(List.class); mockedList.add("one"); mockedList.clear(); //验证add("one")和clear()行为是否发生 verify(mockedList).add("one"); verify(mockedList).clear(); }
2、验证返回值
@Test public void two(){ //模拟创建一个List对象 LinkedList mockLinkedList = mock(LinkedList.class); //打桩,当LinkedList调用get(0)方法时,第一次返回hello,第二次n次返回world when(mockLinkedList.get(0)).thenReturn("hello").thenReturn("world"); String result = mockLinkedList.get(0) + " " + mockLinkedList.get(0) + " " + mockLinkedList.get(0); //使用mock对象 System.out.println(mockLinkedList.get(0)); System.out.println(mockLinkedList.get(0)); System.out.println(mockLinkedList.get(0)); assertEquals("hello world world",result); }
这里注意所有的方法都会有返回值,如果没有设置返回值,那么就会返回null或者空集、适当的类型。 Stubbing可以被重写,也就是同一个参数方法可以放回不同的值,但是以最后一次设置的值为标准。一旦被 Stubbed,无论方法被调用多少次,都只会返回Stubbed value。最后一次最重要原则。
第一次运行打印 hello world world,这是第二次运行测试打印 world world world。
3、参数匹配
@Test public void with_arguments(){ Comparable comparable = mock(Comparable.class); //预设根据不同的参数返回不同的结果 when(comparable.compareTo("Test")).thenReturn(1); when(comparable.compareTo("Omg")).thenReturn(2); assertEquals(1, comparable.compareTo("Test")); assertEquals(2, comparable.compareTo("Omg")); //对于没有预设的情况会返回默认值 assertEquals(0, comparable.compareTo("Not stub")); }
除了匹配给定参数外,还可以使用参数匹配器 argumentMatchers,更加灵活
@Test public void argumentMatchersTest(){ List list = mock(List.class); //匹配任意int类型的值 when(list.get(anyInt())).thenReturn(1); assertEquals(1, list.get(1)); verify(list).get(anyInt()); assertEquals(1, list.get(999)); }
若方法中的某一个参数使用了matcher,则所有的参数都必须使用matcher:
@Test public void argumentMatchersTest2(){ Map map = mock(Map.class); //匹配任意参数 when(map.put(anyInt(),anyString())).thenReturn("world"); assertEquals("world", map.put(1,"hello")); //以下三种验证都可通过测试 verify(map).put(anyInt(),anyString()); verify(map).put(anyInt(),eq("hello")); verify(map).put(eq(1),eq("hello")); }
4、验证调用次数
@Test public void three(){ List mockedList = mock(List.class); mockedList.add(1); mockedList.add(2); mockedList.add(2); mockedList.add(3); mockedList.add(3); mockedList.add(3); //验证是否被调用一次,等效于下面的times(1),默认的,可以不写 verify(mockedList).add(1); verify(mockedList,times(1)).add(1); //验证是否被调用2次 verify(mockedList,times(2)).add(2); //验证是否被调用3次 verify(mockedList,times(3)).add(3); //验证是否从未被调用过 verify(mockedList,never()).add(4); //验证至少调用一次 verify(mockedList,atLeastOnce()).add(1); //验证至少调用2次 verify(mockedList,atLeast(2)).add(2); //验证至多调用3次 verify(mockedList,atMost(3)).add(3); }
5、模拟抛出异常
@Test(expected = IOException.class) public void when_thenThrow() throws IOException { OutputStream outputStream = mock(OutputStream.class); //预设当流关闭时抛出异常 ,因为close()返回类型为void,采用了doThrow 形式 doThrow(new IOException()).when(outputStream).close(); outputStream.close(); }
对于返回类型为void的方法,有系列函数可以用来处理,如doThrow, doAnswer, doNothing, doReturn。
6、验证执行的顺序
@Test public void four(){ List firstList = mock(List.class); List secondList = mock(List.class); //using mocks firstList.add("was called first one mock"); firstList.add("was called second one mock"); secondList.add("was called third one mock"); secondList.add("was called fourth one mock"); //create inOrder object passing any mocks that need to be verified in order // InOrder indOrder = inOrder(firstList,secondList); InOrder indOrder = inOrder(secondList,firstList); //实际上两种顺序都通过测试了 indOrder.verify(firstList).add("was called first one mock"); indOrder.verify(firstList).add("was called second one mock"); indOrder.verify(secondList).add("was called third one mock"); indOrder.verify(secondList).add("was called fourth one mock"); }
7、验证零互动
@Test public void verify_interaction(){ List list = mock(List.class); List list2 = mock(List.class); List list3 = mock(List.class); list.add(1); verify(list).add(1); verify(list,never()).add(2); //验证零互动行为 verifyZeroInteractions(list2,list3); }
8、找出冗余的互动(即未被验证到的)
@Test public void find_redundant_interaction(){ List list = mock(List.class); list.add(1); list.add(2); verify(list,times(2)).add(anyInt()); //检查是否有未被验证的互动行为,因为add(1)和add(2)都会被上面的anyInt()验证到,所以下面的代码会通过 verifyNoMoreInteractions(list); //下面因为只验证了add(1),add(2)没有被验证,所以下面的代码会失败抛出异常 // List list2 = mock(List.class); // list2.add(1); // list2.add(2); //verify(list2).add(1); // verifyNoMoreInteractions(list2); }
9、使用注解来快速模拟
在上面的测试中我们在每个测试方法里都mock了一个List对象,为了避免重复的mock,是测试类更具有可读性,我们可以使用下面的注解方式来快速模拟对象:
public class MockDemoTest2 { @Mock private List mockList; public MockDemoTest2(){ MockitoAnnotations.initMocks(this); } @Test public void shorthand(){ mockList.add(1); verify(mockList).add(1); } }
注意要在构造函数中初试化mock对象,否则mock对象为null。也可以通过在类上使用注解:@RunWith(MockitoJUnitRunner.class) ,这样就不需要初始化mock了。
@RunWith(MockitoJUnitRunner.class) public class MockDemoTest2 { @Mock private List mockList; /* public MockDemoTest2(){ MockitoAnnotations.initMocks(this); } */ @Test public void shorthand(){ mockList.add(1); verify(mockList).add(1); } }
10、连续调用
@Test(expected = RuntimeException.class) public void consecutive_calls(){ List mockList = mock(List.class); //模拟连续调用返回期望值,如果分开,则只有最后一个有效 when(mockList.get(0)).thenReturn(0); when(mockList.get(0)).thenReturn(1); when(mockList.get(0)).thenReturn(2); when(mockList.get(1)).thenReturn(0).thenReturn(1).thenThrow(new RuntimeException()); assertEquals(2,mockList.get(0)); assertEquals(2,mockList.get(0)); assertEquals(0,mockList.get(1)); assertEquals(1,mockList.get(1)); //第三次或更多调用都会抛出异常 mockList.get(1); }
11、使用回调生成期望值
@Test(expected = RuntimeException.class) public void consecutive_calls(){ List mockList = mock(List.class); //模拟连续调用返回期望值,如果分开,则只有最后一个有效 when(mockList.get(0)).thenReturn(0); when(mockList.get(0)).thenReturn(1); when(mockList.get(0)).thenReturn(2); when(mockList.get(1)).thenReturn(0).thenReturn(1).thenThrow(new RuntimeException()); assertEquals(2,mockList.get(0)); assertEquals(2,mockList.get(0)); assertEquals(0,mockList.get(1)); assertEquals(1,mockList.get(1)); //第三次或更多调用都会抛出异常 mockList.get(1); }
12、监控真实对象
当使用spy的时候真正的方法将会被调用,而不再是stub的对象了,这个和部分mock的思想是一样的。
@Test public void seven(){ List list = new LinkedList(); List spy = spy(list); //optionally, you can stub out some methods: when(spy.size()).thenReturn(100); //using the spy calls real methods spy.add("one"); spy.add("two"); //prints "one" - the first element of a list System.out.println(spy.get(0)); //size() method was stubbed - 100 is printed System.out.println(spy.size()); //optionally, you can verify verify(spy).add("one"); verify(spy).add("two"); }
使用spy的时候需要注意一点:有时不能用when-then语句,而改用do-when语句
@Test(expected = IndexOutOfBoundsException.class) public void spy_on_real_objects(){ List list = new LinkedList(); List spy = spy(list); //下面预设的spy.get(0)会报错,因为会调用真实对象的get(0),所以会抛出越界异常 //when(spy.get(0)).thenReturn(3); //使用doReturn-when可以避免when-thenReturn调用真实对象api doReturn(999).when(spy).get(999); //预设size()期望值 when(spy.size()).thenReturn(100); //调用真实对象的api spy.add(1); spy.add(2); assertEquals(100,spy.size()); assertEquals(1,spy.get(0)); assertEquals(2,spy.get(1)); verify(spy).add(1); verify(spy).add(2); assertEquals(999,spy.get(999)); spy.get(2); }
13、修改对未预设的调用返回默认期望值
@Test public void eight(){ //mock对象使用Answer来对未预设的调用返回默认期望值 List mocklist = mock(List.class,new Answer(){ public Object answer(InvocationOnMock invocation) throws Throwable { return 999; } }); //下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值 assertEquals(999, mocklist.get(1)); //下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值 assertEquals(999,mocklist.size()); }
14、捕获参数来进一步断言
@Test public void capturing_args(){ PersonDao personDao = mock(PersonDao.class); PersonService personService = new PersonService(personDao); ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class); personService.update(1,"jack"); verify(personDao).update(argument.capture()); assertEquals(1,argument.getValue().getId()); assertEquals("jack",argument.getValue().getName()); } class Person{ private int id; private String name; Person(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public String getName() { return name; } } interface PersonDao{ public void update(Person person); } class PersonService{ private PersonDao personDao; PersonService(PersonDao personDao) { this.personDao = personDao; } public void update(int id,String name){ personDao.update(new Person(id,name)); } }
15、真实的部分mock
@Test public void real_partial_mock(){ //通过spy来调用真实的api List list = spy(new ArrayList()); assertEquals(0,list.size()); A a = mock(A.class); //通过thenCallRealMethod来调用真实的api when(a.doSomething(anyInt())).thenCallRealMethod(); assertEquals(999,a.doSomething(999)); } class A{ public int doSomething(int i){ return i; } }
16、重置mock
@Test public void reset_mock(){ List list = mock(List.class); when(list.size()).thenReturn(10); list.add(1); assertEquals(10,list.size()); verify(list).add(1); //重置mock,清除所有的互动和预设 reset(list); assertEquals(0,list.size()); }