一.介绍
mockito 是java流行的mock框架,通过该框架来完成一些集成测试和单元测试。
二.原理
mocktio主要是使用代理模式来设计,mockitio采用byte buddy框架生成一个动态代理对象,为mock对象的每个方法都做了拦截。只要调用mock对象则会直接调用拦截器方法。
mocktio主要流程分为以下三部分,也是主要使用方式:
- 创建mock对象
- 设置mock行为
- 执行mock方法
这三个步骤代码如下:1
2
3
4
5
6
7
8// 1. 创建mock对象
List list = Mockito.mock(List.class);
// 2. 设置mock行为
Mockito.when(mock.add("test")).thenReturn(true);
// 3. 执行mock方法
System.out.println(mock.add("test"));
本文将根据以上三个步骤来解析整个过程。
2.1 数据结构
首先需要说明mocktio内部的数据结构
MockHandler
mockito会为每个创建的mock对象创建一个MockHandler, 它的实现类是MockHandlerImpl。该对象主要用于处理mock对象的每个被拦截方法。执行每个拦截方法的时候,都会交给这个handler处理。
一个mock对象对应一个MockHandler
该方法中有两个重要的结构:
- OngoingStubbingImpl 是一个桩实现,是mock方法的一个包装。会为每个mock对象的方法创建一个OngoingStubbingImpl,用来监控和模拟该方法行为。如上面mock.add(“test”)行为。一个mock方法对应多个OngoingStubbingImpl,其实每调用一次mock方法都会创建一个OngoingStubbingImpl对象。
- MockingProgress。 用来存放正在mock中的数据,如OngoingStubbingImpl。采用了ThreadLocal方式实现,保证了线程安全。
2.2 创建mock对象
mock对象的创建主要是为了创建一个mock对象的代理类,使得代理类代理mock对象的所有方法(从源码设计中包含了私有方法,但是受私有方法封装性原因,该私有方法无法进行mock).
根据代理模式,要实现这个过程首先是需要创建一个类,该类继承了被代理类,并且对每个方法进行拦截。在拦截方法中实现mock逻辑。
mock实例方法的场景利用了Java运行时多态的原理,通过重写父类的方法来修改某个方法的行为。
mockito首先接受一个指定的class类型,采用java的动态代理逻辑,使用了ByteBuddy框架来生成该class类型的代理实例。代理对象继承了被mock类,为了对每个方法做拦截,通过byte buddy设置拦截器 MockMethodInterceptor。MockMethodInterceptor的主要作用是将mock对象的拦截方法执行交给了MockHandler来处理。
1 | // 使用byte buddy 生成一个class |
2.3 设置mock行为
该部分主要分为三个步骤:
- mock.doSome(): 为mock方法设置OngoingStubbingImpl,并存放在ThreadLocal中。
- 执行when方法:执行when方法从mock对象的ThreadLocal中获得mock方法的桩对象OngoingStubbingImpl
- 设置mock行为。对OngoingStubbingImpl设置mock行为,如thenReturn、thenThrow等。
when
1 | Mockito.when(mock.add("test")).thenReturn(true); |
执行以上代码,如何为mock.add(“test”)方法设置拦截,使得调用mock.add(“test”)时返回true呢?when的方法
其主要巧妙的运用的ThreadLocal特性,同时通过对使用规范的限制来保证整个逻辑能够正确执行。
在执行when的时候 mocktio要求执行mock对象的mock方法,即mock.doSome(),如mock.add(“test”)。 执行完成后,在执行when,通过when方法获取该mock方法对应的OngoingStubbingImpl,用于后面设置对应的操作。
在执行 mock.doSome()
thenReturn
参考
- Mockito 源码解析: https://toutiao.io/posts/b0h8dz/preview
- ByteBuddy源码