Mockito是Java平台上超火的Mock框架,因为其便捷的API,深受广大开发者喜爱。本文将从源码的角度,来分析Mockito的运行流程。
Mockito 简介
Mockito类相当于整个框架的门面,负责对外提供调用接口。常用的有如下几个:
mock
1List list = Mockito.mock(List.class); 此时, list就是被Mockito所mock后生成的实例,Mockito会记住它所mock对象的所有调用,为后面的验证做准备。- 默认情况下,调用mock对象的带返回值的方法会返回默认的值,比如返回null、0值或者false等。
- 允许多次代理mock对象的同一个方法,但具体的行为取决于该方法最近的一次代理行为。
- mock对象的代理方法,允许多次调用,只有不覆盖它的代理行为,那么每次调用的执行相同的行为或者返回相同的值
- 相同的方法和参数唯一确认一个代理。比如你可以分别代理get(int)方法在参数分别为0和1时的不同行为。
when
1Mockito.when(list.size()).thenReturn(1);上述代码表示,当对list对象调用size()方法时,会返回1.这样我们就可以自定义mock对象的行为了。
java 中的程序调用是以栈的形式实现的,对于 when 方法,list.size() 方法的调用对它是不可见的。when 能接收到的,只有 list.size() 的返回值。
verify
1Mockito.verify(list).add(Matchers.anyObject());verify是负责验证的函数,接受的参数是一个被mock的对象,表示验证其是否执行了后面的方法。
Mockito 实现
Mock 使用了代理模式。我们操作的list,实际上是继承了List
的代理类的实例,我们把它称为 proxy 对象。因此对于list 的所有操作,都会先经过 proxy 对象。
Mocktio 就是通过这种方式,拦截到了list 的所有操作。只要在调用list 的方法时,将该方法存放起来,然后在调用 thenReturn 为其设置返回值就可以了。
类图
几个主要的类已用红颜色标出,它们将会是Mockito的核心,也是下文主要介绍的对象。接下来,就深入Mockito的源码来一探究竟。
Mock
Mockito类中有几个mock函数的重载,最终都会调到mock(ClassClass
类型与一个MockSettings
,class就是我们需要mock对象的类型,而mockSettings则记录着此次mock的一些信息。mock的行为实则转交个MockitoCore
:
|
|
在MockitoCore
中一是做了一下初始化工作,二是继续转交给MockUtil
处理:
|
|
在MockUtil
中则作了两件非常关键的事情:
|
|
一是创建了MockHandler对象,MockHandler是一个接口,通过createMockHandler函数我们可以清楚,MockHandler对象的实例是InvocationNotifierHandler类型,但它只是负责对外的包装,内部实际起作用的是MockHandlerImpl这个家伙,也是上文类图右下角的那位,这个类可谓承载了Mockito的主要逻辑,我们后面会详细的来说明。
接着会调用mockMaker来创建最终的实例,这个MockMaker
也是一个接口,其实现为ByteBuddyMockMaker
,我们追进去看一下createMock函数(省略异常处理代码):
|
|
首先函数内的第一行代码就比较引人注目:createProxyClass,要创建一个代理类, 但没有创建出目标类的实例。所以往下看,这个重担就交在了这三行代码上:
|
|
这个Instantiator
也是一个接口,它有两个实现,一是ObjenesisInstantiator
,另外一个是ConstructorInstantiator
, 默认情况下,都是在使用ObjenesisInstantiator
, 所以我们这里只查看ObjenesisInstantiator
,并且ObjenesisInstantiator
也是上述UML图中的一个标红的类,因为它承载生成对象的工作。看其newInstance方法:
|
|
这里调用了objenesis.newInstance,可以根据不同的平台选择不同的方法来new对象。总之一句话,只要输入一个class进去,它就会输出其一个实例对象.可以查看其Github:https://github.com/easymock/objenesis。
如此,走到这里,我们的mock对象就已经被生成出来.
When
接下来看一下Mockito里的when方法:
|
|
同样是转交到了MOCKITO_CORE
这里:
|
|
上文说过,mock对象所有的方法最终都会交由MockHandlerImpl
的handle方法处理.
在handle中和when有关的几句代码是:
|
|
when调用的基本形式是when(mock.doSome()), 此时,当mock.doSome()时即会触发上面的语句,OngoingStubbingImpl
表示正在对一个方法打桩的包装,invocationContainerImpl
相当于一个mock对象的管家,记录着mock对象方法的调用。后面我们还会再见到它。mockingProgress
则可以理解为一个和线程相关的记录器,用于存放每个线程正要准备做的一些事情,它的内部包含了几个report 和 pull 这样的函数,如上所看到,mockingProgress记录着ongoingStubbing对象。
再回过头来看MOCKITO_CORE
里的when方法就会清晰许多,它会取出刚刚存放在mockingProgress中的ongoingStubbing对象。OngoingStubbing<T> stubbing = mockingProgress.pullOngoingStubbing();
而我们熟知的thenReturn
、thenThrow
则是OngoingStubbing
里的方法,这些方法最终都会调到如下方法:
|
|
answer
即是对thenReturn
、thenThrow
之类的包装,当然我们也可以自己定义answer。我们又看到了invocationContainerImpl
,它会帮我们保管这个answer
,待以后调用该方法时返回正确的值,与之对应的代码是handle方法中如下这句代码:
|
|
当stubbedInvocation不为空时,就会调用anwser方法来回去之前设定的值:
|
|
如此,对于when(mock.doSome()).thenReturn(obj)这样的用法的主要逻辑了。