mockito使用速查手册

1. 依赖

1.1 pom.xml

<dependencies>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.9.5</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

1.2 引用

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

2. 基本使用实例

2.1 验证行为

ArrayList mock = mock(ArrayList.class);
//使用mock的对象
mock.add(1);
mock.clear();
//验证add(1)和clear()行为是否发生
verify(mock).add(1);
verify(mock).clear();

2.2 模拟我们所期望的结果

例一

ArrayList mock = mock(ArrayList.class);
// 预设第一次调用add(1)时返回true,之后调用返回false
when(mock.add(1)).thenReturn(true).thenReturn(false);
assertTrue(mock.add(1));
assertFalse(mock.add(1));

例二

// 预设抛出NullPointerException
@org.junit.Test(expected = NullPointerException.class)
public void test() {
    ArrayList mock = mock(ArrayList.class);
    // 预设当调用mock.add(1)时抛出NPE异常
    doThrow(new NullPointerException()).when(mock).add(1);
    // 不能写成下面这种方式
    // doThrow(new NullPointerException()).when(mock.add(1));
    mock.add(1);
}

2.3 使用Answer接口的实现来mock数据

RETURNS_SMART_NULLS

RETURNS_SMART_NULLS实现了Answer接口的对象,它是创建mock对象时的一个可选参数,mock(Class,Answer)。

通过RETURNS_SMART_NULLS参数创建的mock对象在没有调用stubbed方法时会返回SmartNull。例如:返回类型是String,会返回"";是int,会返回0;是List,会返回空的List。另外,在控制台窗口中可以看到SmartNull的友好提示。

List mock = mock(List.class, RETURNS_SMART_NULLS);

// mock.get(2);
//使用RETURNS_SMART_NULLS参数创建的mock对象,不会抛出NullPointerException异常。
// 另外控制台窗口会提示信息“SmartNull returned by unstubbed get() method on mock”
System.out.println(mock.get(2));

RETURNS_DEEP_STUBS

RETURNS_DEEP_STUBS也是创建mock对象时的备选参数

RETURNS_DEEP_STUBS参数程序会自动进行mock所需的对象,方法test和test2是等价的

@org.junit.Test
public void test() {
    School school = mock(School.class, RETURNS_DEEP_STUBS);
    when(school.getStu().getName()).thenReturn("zs");
    System.out.println(school.getStu().getName());
}

@org.junit.Test
public void test2() {
    School school = mock(School.class);
    Student stu = mock(Student.class);
    when(school.getStu()).thenReturn(stu);
    when(stu.getName()).thenReturn("zs2");
    System.out.println(school.getStu().getName());
}  

2.4 模拟方法抛出异常

// 期望抛出NPE异常
@org.junit.Test(expected = NullPointerException.class)
public void test2() {
    List list = mock(List.class);
    doThrow(new NullPointerException()).when(list).add(1);
    list.add(1);
}

2.5 使用注解快速模拟

在基类中添加初始化mock的代码

public class Test {
    @Mock
    private List list;

    public Test() {
        // 在基类中添加初始化mock的代码
        MockitoAnnotations.initMocks(this);
    }

    // 预设抛出NullPointerException
    @org.junit.Test
    public void test() {
        list.add(1);
        verify(list).add(1);
    }
}

使用built-in runner:MockitoJUnitRunner

@RunWith(MockitoJUnitRunner.class)
public class Test {
    @Mock
    private List list;

    // 预设抛出NullPointerException
    @org.junit.Test
    public void test() {
        list.add(1);
        verify(list).add(1);
    }
}

2.6 参数匹配

匹配指定参数

List list = mock(List.class);
//预设根据不同的参数返回不同的结果
when(list.add(1)).thenReturn(true);
when(list.add(2)).thenReturn(false);
System.out.println(list.add(1));
System.out.println(list.add(2));
//对于没有预设的情况会返回默认值
System.out.println(list.add(3));

匹配任意参数

@RunWith(MockitoJUnitRunner.class)
public class Test {
    @org.junit.Test
    public void test() {
        List list = mock(List.class);
        when(list.set(anyInt(), anyObject())).thenReturn("success");
        when(list.add(argThat(new IsValid()))).thenReturn(true);
        System.out.println(list.set(0, "fdsfasf"));
        System.out.println(list.add(null));
    }
}

class IsValid extends ArgumentMatcher<Object> {
    @Override
    public boolean matches(Object o) {
        return o == null;
    }
}

注意事项

如果你使用了参数匹配,那么所有的参数都必须通过matchers来匹配

List list = mock(List.class);
when(list.set(anyInt(), anyObject())).thenReturn("success");

// 以下写法运行报错,如果你使用了参数匹配,那么所有的参数都必须通过matchers来匹配
// when(list.set(anyInt(), new Object())).thenReturn("success");
System.out.println(list.set(0, "fdsfasf"));
System.out.println(list.add(null));

当我们使用参数匹配器时,所有参数都应使用匹配器。 如果要为参数使用特定值,则可以使用eq()方法。

List list = mock(List.class);
when(list.set(anyInt(), eq("ttt"))).thenReturn("success");

System.out.println(list.set(1, "ttt"));

2.7 捕获参数来进一步断言

较复杂的参数匹配器会降低代码的可读性,有些地方使用参数捕获器更加合适。

  • argument.capture() 捕获方法参数
  • argument.getValue() 获取方法参数值,如果方法进行了多次调用,它将返回最后一个参- 数值
  • argument.getAllValues() 方法进行多次调用后,返回多个参数值
List mock = mock(List.class);
List mock2 = mock(List.class);
mock.add("John");
mock2.add("Brian");
mock2.add("Jim");

ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);

// 获取mock.add中的John赋值给argument的值
verify(mock).add(argument.capture());
assertEquals("John", argument.getValue());

verify(mock2, times(2)).add(argument.capture());

assertEquals("Jim", argument.getValue());
assertArrayEquals(new Object[]{"Brian","Jim"},argument.getAllValues().toArray());

2.8 使用方法预期回调接口生成期望值(Answer结构)

@RunWith(MockitoJUnitRunner.class)
public class Test {
    @org.junit.Test
    public void test() {
        List mockList = mock(List.class);

        when(mockList.get(anyInt())).thenAnswer(new CustomAnswer());
        assertEquals("hello world:0",mockList.get(0));
        assertEquals("hello world:999",mockList.get(999));
    }
}

class CustomAnswer implements Answer<String> {
    @Override
    public String answer(InvocationOnMock invocation) throws Throwable {
        Object[] args = invocation.getArguments();
        return "hello world:"+args[0];
    }
}

2.9 修改对未预设的调用返回默认期望

//mock对象使用Answer来对未预设的调用返回默认期望值
List mock = mock(List.class,new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return 999;
    }
});
//下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值
assertEquals(999, mock.get(1));
//下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值
assertEquals(999,mock.size());
// 下述方法报错,报错“Integer无法转化为Boolean”
// System.out.println(mock.contains(null));

2.10 用spy监控真实对象

  • Mock不是真实的对象,它只是用类型的class创建了一个虚拟对象,并可以设置对象行为
  • Spy是一个真实的对象,但它可以设置对象行为
  • InjectMocks创建这个类的对象并自动将标记@Mock、@Spy等注解的属性值注入到这个中
import static org.mockito.Mockito.*;
...

@org.junit.Test(expected = IndexOutOfBoundsException.class)
public void test() {
    List list = new LinkedList();
    List spy = spy(list);
    //下面预设的spy.get(0)会报错,因为会调用真实对象的get(0),所以会抛出越界异常
    // 用thenReturn 会走go()方法体,然后将返回值Mock掉
    //when(spy.get(0)).thenReturn(3);

    //使用doReturn-when可以避免when-thenReturn调用真实对象api
    // 用doReturn 不走go()方法体
    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);
}

2.11 真实的部分mock

@RunWith(MockitoJUnitRunner.class)
public class Test {
    @org.junit.Test // (expected = IndexOutOfBoundsException.class)
    public void test() {
        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;
    }
}

2.12 重置mock

List list = mock(List.class);
when(list.add(1)).thenReturn(true);
System.out.println(list.add(1));
reset(list);
when(list.add(1)).thenReturn(false);
System.out.println(list.add(1));

2.13 验证确切的调用次数

List list = mock(List.class);
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(3);
list.add(3);
//验证是否被调用一次,等效于下面的times(1)
verify(list).add(1);
verify(list,times(1)).add(1);
//验证是否被调用2次
verify(list,times(2)).add(2);
//验证是否被调用3次
verify(list,times(3)).add(3);
//验证是否从未被调用过
verify(list,never()).add(4);
//验证至少调用一次
verify(list,atLeastOnce()).add(1);
//验证至少调用2次
verify(list,atLeast(2)).add(2);
//验证至多调用3次
verify(list,atMost(3)).add(3);

2.14 连续调用

List list = mock(List.class);
//模拟连续调用返回期望值,如果分开,则只有最后一个有效
when(list.get(0)).thenReturn(0);
when(list.get(0)).thenReturn(1);
when(list.get(0)).thenReturn(2);
when(list.get(1)).thenReturn(0).thenReturn(1).thenThrow(new RuntimeException());
assertEquals(2,list.get(0));
assertEquals(2,list.get(0));
assertEquals(0,list.get(1));
assertEquals(1,list.get(1));
//第三次或更多调用都会抛出异常
list.get(1);

2.15 验证执行顺序

List list = mock(List.class);
List list2 = mock(List.class);
list.add(1);
list2.add("hello");
list.add(2);
list2.add("world");
//将需要排序的mock对象放入InOrder
InOrder inOrder = inOrder(list,list2);
//下面的代码不能颠倒顺序,验证执行顺序
inOrder.verify(list).add(1);
inOrder.verify(list2).add("hello");
inOrder.verify(list).add(2);
inOrder.verify(list2).add("world");

2.16 确保模拟对象上无互动发生

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

2.17 找出冗余的互动(即未被验证到的)

List list = mock(List.class);
list.add(1);
list.add(2);
verify(list,times(2)).add(anyInt());
//检查是否有未被验证的互动行为,因为add(1)和add(2)都会被上面的anyInt()验证到,所以下面的代码会通过
verifyNoMoreInteractions(list);

List list2 = mock(List.class);
list2.add(1);
list2.add(2);
verify(list2).add(1);
//检查是否有未被验证的互动行为,因为add(2)没有被验证,所以下面的代码会失败抛出异常
verifyNoMoreInteractions(list2);
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值