其他分享
首页 > 其他分享> > 模拟测试框架之Mockito使用及原理分析

模拟测试框架之Mockito使用及原理分析

作者:互联网

前言

当我们进行单元测试时,可能某个依赖的服务还没有开发完成(如RPC或HTTP调用),这种情况下我们就可以对依赖服务创建一个模拟对象,这样我们就可以更加关注于当前的测试类,而不是依赖的服务类。Mockito是一个强大的模拟测试框架,可以让我们很方便的创建模拟对象并进行行为验证。

添加maven依赖

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>3.3.3</version>
</dependency>

创建模拟对象

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.mockito.Mockito;

public class TestMock2 {

  public static void main(String[] args) {
    List<String> mockList = Mockito.mock(List.class);
    System.out.println(mockList.getClass());
    System.out.println(Arrays.toString(mockList.getClass().getInterfaces()));
    mockList = Mockito.mock(ArrayList.class);
    System.out.println(mockList.getClass());
    System.out.println(Arrays.toString(mockList.getClass().getInterfaces()));
  }

}

既可以对接口创建模拟对象,也可以对具体的实现类创建模拟对象,Mockito底层使用ByteBuddy库来创建代理类,使用Objenesis库来实例化对象。
ByteBuddy是一个代码生成和操作的类库,类似于Cglib、javassist,底层也是ASM库,官网
Objenesis是一个小的java库,可以让我们绕过构造器来实例化对象,Spring通过Cglib创建代理对象的过程中就使用到了Objenesis,
更多信息可以查看java中Objenesis库简单使用

创建部分模拟对象

import java.util.ArrayList;
import java.util.List;
import org.mockito.Mockito;

public class TestMock4 {

  public static void main(String[] args) {
    List<String> mockList = Mockito.mock(ArrayList.class);
    List<String> spyList = Mockito.spy(ArrayList.class);
    mockList.add("hello");
    spyList.add("hello");
    System.out.println(mockList.get(0));//null
    System.out.println(spyList.get(0));//hello
  }

}

spy()方法和mock()的区别在于

配置方法行为

import java.util.List;
import org.mockito.Mockito;

public class TestMock2 {

  public static void main(String[] args) {
    List<String> mockList = Mockito.mock(List.class);
    Mockito.when(mockList.size()).thenReturn(1);
    System.out.println(mockList.size());
    Mockito.when(mockList.get(0)).thenReturn("hello");
    System.out.println(mockList.get(0));
    //抛出异常
    Mockito.when(mockList.get(0)).thenThrow(new RuntimeException("error"));
    System.out.println(mockList.get(0));
  }

}

为List.size()行为配置一个结果1,下次调用此方法时直接返回此结果。
没有配置行为时,使用默认结果,配置在DefaultMockitoConfiguration的ReturnsEmptyValues类中,如对int值返回0,对Iterable类型,返回一个空的ArrayList。
接下来分析一下原理:

  1. 当我们调用List.size()时,Mockito会拦截此方法,将方法调用(List.size())的详细信息保存到模拟对象的上下文中
  2. 调用Mockito.when()方法时,会从上下文(具体为MockingProgressImpl)中获取到最后一次方法调用(List.size())信息
  3. 将thenReturn()的参数作为结果保存起来,下次调用时直接获取。

方法调用容器为InvocationContainerImpl,其中包含一系列StubbedInvocationMatcher对象,每一个对象都是一个方法调用和具体结果的封装。

验证方法调用次数

import java.util.List;
import org.mockito.Mockito;

public class TestMock3 {

  public static void main(String[] args) {
    List<String> mockList = Mockito.mock(List.class);
    mockList.size();
    mockList.size();
    Mockito.verify(mockList, Mockito.times(2)).size();
  }

}

如果方法没有被调用2次,就会抛出异常。

总结

Mockito基本原理就是对接口或具体类创建动态代理对象,在实际进行方法调用时,会查找是否已经配置了结果,没有就使用默认结果。

参考

Mockito官网
一文让你快速上手 Mockito 单元测试框架
手把手教你 Mockito 的使用

标签:mockList,框架,Mockito,List,println,import,class,模拟
来源: https://www.cnblogs.com/strongmore/p/16271972.html