Mockito详解

一. Mock基本介绍

mock的定义(what)
mock就是在测试过程中,对于一些不容易构造/获取的对象,创建一个mock对象来模拟对象的行为
为什么要使用mock(why)
在对代码进行单元测试过程中,经常会有以下的情况发生:

class A 依赖 class B  
class B 依赖 class C和class D  
class C 依赖 ...  
class D 依赖 ...  

1.被测对象依赖的对象构造复杂
我们想对class A进行单元测试,需要构造大量的class B、C、D等依赖造步骤多、耗时较长)
,对象,他们的构造这时我们可以利用mock去构造过程复杂(体现在构虚拟的class B、C、D
对象用于class A的测试,因为我们只是想测试class A的行为是否符合预期,我们并不需要
测试依赖对象。

2.被测单元依赖的模块尚未开发完成,而被测对象需要依赖模块的返回值进行测试:
----- 比如service层的代码中,包含对dao层的调用,但dao层代码尚未实现
----- 比如web的前端依赖后端接口获取数据进行联调测试,但后端接口并未开发完成

哪些时机和场合需要使用mock(when&where)
1.单元测试/接口测试中测试对象依赖其他对象,这些对象的构造复杂、耗时或者根本无法构
造(未交付)
2.我们只测试对象内部逻辑的质量,不关心依赖对象的逻辑正确性和稳定性

mock原则
1.不需要对所有的依赖对象进行mock,只对那些构造复杂、构造耗时较长的依赖进行mock
2.如果做分层自动化,高层的测试设计可以基于以下假设:低层的测试已保证低层对象的质量,高层对低层的依赖可以mock,无需关心所依赖的低层对象的内部逻辑质量

常用mockt技术

  • powermock
  • easymock
  • mockito

二. Mockito介绍

mockito是众多mock技术中的佼佼者,功能强大,api简洁。
相关文档地址:

三、Api

(一)Mockito

org.mockito.Mockito是mockito提供的核心api,提供了大量的静态方法,用于帮助我们来mock对象,验证行为等等,然后需要注意的是,很多方法都被封装在了MockitoCore类里面,下面对一些常用的方法做一些介绍。

  • mock:构建一个我们需要的对象;可以mock具体的对象,也可以mock接口。
  • spy:构建监控对象
  • verify:验证某种行为
  • when:当执行什么操作的时候,一般配合thenXXX 一起使用。表示执行了一个操作之后产生什么效果。
  • doReturn:返回什么结果
  • doThrow:抛出一个指定异常
  • doAnswer:做一个什么相应,需要我们自定义Answer;
  • times:某个操作执行了多少次
  • atLeastOnce:某个操作至少执行一次
  • atLeast:某个操作至少执行指定次数
  • atMost:某个操作至多执行指定次数
  • atMostOnce:某个操作至多执行一次
  • doNothing:不做任何处理
  • doReturn:返回一个结果
  • doThrow:抛出一个指定异常
  • doAnswer:指定一个操作,传入Answer
  • doCallRealMethod:返回真实业务执行的结果,只能用于监控对象

(二)ArgumentMatchers

用于进行参数匹配,减少很多不必要的代码

  • anyInt:任何int类型的参数,类似的还有anyLong/anyByte等等。
  • eq:等于某个值的时候,如果是对象类型的,则看toString方法
  • isA:匹配某种类型
  • matches:使用正则表达式进行匹配

(三)OngoingStubbing

OngoingStubbing用于返回操作的结果。

  • thenReturn:指定一个返回的值
  • thenThrow:抛出一个指定异常
  • then:指定一个操作,需要传入自定义Answer;
  • thenCallRealMethod:返回真实业务执行的结果,只能用于监控对象。

四、使用案例

mockit依赖

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>2.0.2-beta</version>
    <scope>test</scope>
</dependency>

而在springboot中,只要一来了spring-boot-test,就自动了引入了Mockito相关依赖,不需要我们做额外工作。

(一)验证行为

一旦mock对象被创建了,mock对象会记住所有的交互。然后你就可能选择性的验证你感兴趣的交互,也就是说,你对这个Mock对象做的操作,都被记录下来了,验证的时候会是合法的。
示例:

@Test
void testBehavior() {

    //构建moock数据
    List<String> list = mock(List.class);
    list.add("1");
    list.add("2");

    System.out.println(list.get(0)); // 会得到null ,前面只是在记录行为而已,没有往list中添加数据

    verify(list).add("1"); // 正确,因为该行为被记住了
    verify(list).add("3");//报错,因为前面没有记录这个行为

}

(二)Stub(存根/打桩)

存根用于预先说明当执行了什么操作的时候,产生一个什么响应,
存根特点

  • 默认情况下,所有的函数都有返回值。mock函数默认返回的是null,一个空的集合或者一个被对象类型包装的内置类型,例如0、false对应的对象类型为Integer、Boolean;
  • 测试桩函数可以被覆写 : 例如常见的测试桩函数可以用于初始化夹具,但是测试函数能够覆写它。请注意,覆写测试桩函数是一种可能存在潜在问题的做法;
  • 一旦测试桩函数被调用,该函数将会一致返回固定的值;

示例:
常规存根
主要用到when 、thenReturn、then、thenThrow、thenAnswer这几个函数。

@Test
void testStub() {
    List<Integer> l = mock(ArrayList.class);

    when(l.get(0)).thenReturn(10);
    when(l.get(1)).thenReturn(20);
    when(l.get(2)).thenThrow(new RuntimeException("no such element"));

    assertEquals(l.get(0), 10);
    assertEquals(l.get(1), 20);
    assertNull(l.get(4));
    assertThrows(RuntimeException.class, () -> {
        int x = l.get(2);
    });
}

void函数存根
主要用到doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod()等函数,当然,这些函数也可以用于有返回值的函数的存根;需要注意的是,doCallRealMethod不能用于Mock对象,只能用于监察对象。
示例:

@Test
void testVoidStub(){
    List<Integer> l = mock(ArrayList.class);
    doReturn(10).when(l).get(1);
    doThrow(new RuntimeException("you cant clear this List")).when(l).clear();

    assertEquals(l.get(1),10);
    assertThrows(RuntimeException.class,()->l.clear());
}

(三)参数匹配器

在上面的stub测试中,我们需要对我们关系的数据一个一个的进行stub,如果数据过多的时候,会比较麻烦,这个时候,我们可以利用参数匹配器来完成相应的操作,参数既可以用与stub,也可以用于验证的时候。所有的参数匹配器都存放在org.mockito.ArgumentMatchers中。

示例:

@Test
void testMatchers() {
    List<Integer> l = mock(ArrayList.class);
    when(l.get(anyInt())).thenReturn(100);

    assertEquals(l.get(999),100);
}

(四)调用次数

校验某个操作执行的次数
示例:

@Test
void testTimes() {
    List<Integer> l = mock(ArrayList.class);
    when(l.get(anyInt())).thenReturn(100);
    System.out.println(l.get(0));
    System.out.println(l.get(1));
    System.out.println(l.get(1));
    System.out.println(l.get(2));
    System.out.println(l.get(2));
    System.out.println(l.get(2));
    System.out.println(l.get(2));

    verify(l, times(7)).get(anyInt());
    verify(l, atLeastOnce()).get(0);
    verify(l, atLeast(3)).get(2);
    verify(l, atMost(6)).get(2);
    verify(l, atMostOnce()).get(0);
}

(五)简化Mock创建方式

我们可以通过@Mock注解来简化Mock对象的创建过程,这样的话,我们就可以在多个测试中直接共用这些mock对象了,需要注意的是,我们需要在方法开始执行下面的操作:MockitoAnnotations.initMocks

@Mock
private List<Long> longs;

@Test
void testMockitoCreate() {
    when(longs.get(0)).thenReturn(100L);
    assertEquals(longs.get(0),100L);
}

@BeforeEach
void before() {
    MockitoAnnotations.initMocks(this);
}

(六)监控真实对象

我们可以为真实对象创建一个监控(spy)对象,这样,就可以调用它的方法来返回真实业务数据了
示例:

@Test
void testSpy() {

    UserService userService = new UserService();
    UserService spy = spy(userService);
    when(spy.hello("liuxing")).thenCallRealMethod();

    when(spy.hello("lx")).thenReturn("haha,lx");

    assertEquals(spy.hello("liuxing"), "Hello,liuxing");
    assertEquals(spy.hello("lx"), "hello,lx");
}

class UserService {
    public String hello(String name) {
        return "Hello," + name;
    }
}

和创建mock对象一样,我们可以使用@Spy注解来简化创建过程,如下:

@Spy UserService uService;
@Test
void testSpyWithAnnotation() {
    when(uService.hello("liuxing")).thenCallRealMethod();
    when(uService.hello("lx")).thenReturn("haha,lx");
    assertEquals(uService.hello("liuxing"), "Hello,liuxing");
    assertEquals(uService.hello("lx"), "haha,lx");
}
  • 13
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值