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());  
    }  

 

posted @ 2016-08-31 22:30  mutianzhixi  阅读(259)  评论(0编辑  收藏  举报