Mockito源码解析

193 阅读3分钟

一、创建mock实例

以下以最基础只引入Mockito的场景为例,创建mock实例的时序图如下:

sequenceDiagram
	participant Test
	participant Mockito
	participant MockitoCore
	participant MockUtil
	participant MockHandlerFactory
	participant ByteBuddyMockMaker
	participant SubclassByteBuddyMockMaker
	participant TypeCachingBytecodeGenerator
	participant SubclassBytecodeGenerator
	participant ObjenesisInstantiator
	
	activate Mockito
	Test ->> Mockito : T mock(classToMock)
	activate Mockito
	Mockito ->> Mockito : T mock(classToMock, mockSettings)
	Mockito ->> MockitoCore : T mock(typeToMock, settings)
	MockitoCore ->> MockUtil : T createMock(settings)
	MockUtil ->> MockHandlerFactory : MockHandler<T> <br/>createMockHandler(settings)
	Note over MockUtil,MockHandlerFactory: 创建MockHandler。MockHandler代理<br/>对mock对象的调用,是Mockito的核心。
	MockHandlerFactory -->> MockUtil : MockHandler<T>
	MockUtil ->> ByteBuddyMockMaker : T createMock(settings, handler)
	ByteBuddyMockMaker ->> SubclassByteBuddyMockMaker : T createMock(settings, handler)
	activate SubclassByteBuddyMockMaker
	activate SubclassByteBuddyMockMaker
	SubclassByteBuddyMockMaker ->> SubclassByteBuddyMockMaker : Class createMockType(settings)
	Note over SubclassByteBuddyMockMaker,SubclassByteBuddyMockMaker: 获取织入MockHandler的代理类
	SubclassByteBuddyMockMaker ->> TypeCachingBytecodeGenerator : Class mockClass(params)
	TypeCachingBytecodeGenerator ->> SubclassBytecodeGenerator : Class mockClass(features)
	Note over SubclassBytecodeGenerator,TypeCachingBytecodeGenerator: 使用ByteBuddy创建被mock的类的子类,<br/>并将MockHandler织入,代理原方法调用;
	SubclassBytecodeGenerator -->> TypeCachingBytecodeGenerator : 织入MockHandler的代理类
	TypeCachingBytecodeGenerator -->> SubclassByteBuddyMockMaker : 织入MockHandler的代理类
	deactivate SubclassByteBuddyMockMaker
	SubclassByteBuddyMockMaker ->> ObjenesisInstantiator : T newInstance(Class<T> cls)
	Note over ObjenesisInstantiator,SubclassByteBuddyMockMaker: 绕过类的构造函数直接实例化对象
	ObjenesisInstantiator -->> SubclassByteBuddyMockMaker : mockInstance
	SubclassByteBuddyMockMaker ->> SubclassByteBuddyMockMaker : ((MockAccess) mockInstance)<br/>.setMockitoInterceptor(new MockMethodInterceptor(handler, settings));
	Note over SubclassByteBuddyMockMaker,SubclassByteBuddyMockMaker: 将MockHandler实例包装成MockMethodInterceptor<br/>实例后注入到mockInstance
	SubclassByteBuddyMockMaker -->> ByteBuddyMockMaker : mockInstance
	deactivate SubclassByteBuddyMockMaker
	ByteBuddyMockMaker -->> MockUtil : mockInstance
	MockUtil -->> MockitoCore : mmockInstance
	MockitoCore -->> Mockito : mockInstance
	deactivate Mockito
	Mockito -->> Test : mockInstance
	deactivate Mockito

整个创建过程比较长,核心动作已经在图上标注,总结下来,可以分为下面3步:

  1. 创建一个MockHandler实例, 这是Mockito的核心,负责处理对mock对象的调用;
  2. 使用ByteBuddy创建被mock的类的子类,并将MockHandler织入,代理原方法调用,随后使用Objenesis框架跳过构造创建该类的实例;
  3. 将第1步创建的MockHandler实例包装到MockMethodInterceptor实例后注入到第2步创建的mock实例中;

生成的被mock的类的子类如下:

public class TestMockitoClass$MockitoMock$495200500 extends TestMockitoClass implements MockAccess {
    private static final long serialVersionUID = 42L;
    private MockMethodInterceptor mockitoInterceptor;
    
    public TestMockitoClass$MockitoMock$495200500() {
    }
    
	... 省略其它被代理的方法
        
    public String testMethodString(String var1) {
        return (String)DispatcherDefaultingToRealMethod.interceptSuperCallable(this, this.mockitoInterceptor, cachedValue$kCdhy7AJ$19kbvn3, new Object[]{var1}, new TestMockitoClass$MockitoMock$495200500$auxiliary$N5w85W3v(this, var1));
    }
    
	... 省略其它被代理的方法
    
    public MockMethodInterceptor getMockitoInterceptor() {
        return this.mockitoInterceptor;
    }

    public void setMockitoInterceptor(MockMethodInterceptor var1) {
        this.mockitoInterceptor = var1;
    }
    
    // access flags 0x1010
  final synthetic testMethodString$accessor$4Zz8bn7g(Ljava/lang/String;)Ljava/lang/String;
    ALOAD 0
    ALOAD 1
    INVOKESPECIAL tech/lhzmrl/android/test/testmockito/utils/TestMockitoClass.testMethodString (Ljava/lang/String;)Ljava/lang/String;
    ARETURN
    MAXSTACK = 2
    MAXLOCALS = 2
}
class TestMockitoClass$MockitoMock$495200500$auxiliary$N5w85W3v implements Runnable, Callable, Serializable {
    private TestMockitoClass.MockitoMock.495200500 argument0;
    private String argument1;

    public Object call() throws Exception {
        return this.argument0.testMethodString$accessor$kCdhy7AJ(this.argument1);
    }

    public void run() {
        this.argument0.testMethodString$accessor$kCdhy7AJ(this.argument1);
    }

    TestMockitoClass$MockitoMock$495200500$auxiliary$N5w85W3v(TestMockitoClass.MockitoMock.495200500 var1, String var2) {
        this.argument0 = var1;
        this.argument1 = var2;
    }
}

Tips:

输出ByteBuddy生成的类到文件:

DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
 .subclass(Object.class)
 .name("example.DynamicClass")
 .make();

// 保存到指定目录
dynamicType.saveIn(new File("path/to/output/directory"));

二、方法存根

为mock实例预设方法行为的代码如下:

when(mockInstance.testMethodString(eq("1"))).thenReturn("mock");

这行代码的执行可以分为4部分:

  1. T ArgumentMatchers::eq(T) 执行参数匹配器
  2. TestMockitoClass::testMethodString(String) 执行被存根方法
  3. OngoingStubbing<T> Mockito::when(T) 开启方法存根
  4. OngoingStubbing::thenReturn(T) 预置返回值

1. 执行参数匹配器

sequenceDiagram
	participant Test
	participant ArgumentMatchers
	participant ThreadSafeMockingProgress
	participant MockingProgressImpl
	participant ArgumentMatcherStorageImpl
	participant Stack as Stack#60;LocalizedMatcher#62;
	participant Primitives
	
	activate ArgumentMatchers
	Test ->> ArgumentMatchers : <T> T eq(T)
	Note over ArgumentMatchers,ArgumentMatchers:由T创建判断实例是否相等的参数匹配器
	activate ArgumentMatchers
	ArgumentMatchers ->> ArgumentMatchers : reportMatcher(ArgumentMatcher<?>)
	ArgumentMatchers ->> ThreadSafeMockingProgress : MockingProgress mockingProgress()
	ThreadSafeMockingProgress ->> ArgumentMatchers : MockingProgressImpl
	ArgumentMatchers ->> MockingProgressImpl : ArgumentMatcherStorage getArgumentMatcherStorage()
	MockingProgressImpl ->> ArgumentMatchers : ArgumentMatcherStorageImpl
	ArgumentMatchers ->> ArgumentMatcherStorageImpl : reportMatcher(ArgumentMatcher<?>)
	Note over ArgumentMatcherStorageImpl,ArgumentMatcherStorageImpl:ArgumentMatcher被包装成<br/>LocalizedMatcher,并被压入到栈中
	ArgumentMatcherStorageImpl ->> Stack : E push(E)
	Stack -->> ArgumentMatcherStorageImpl : E
	ArgumentMatcherStorageImpl -->> ArgumentMatchers : 
	deactivate ArgumentMatchers
	ArgumentMatchers ->> Primitives : T defaultValue(Class<T>) 
	Primitives -->> ArgumentMatchers : 返回该类型的一个默认值
	ArgumentMatchers -->> Test : 返回该类型的一个默认值
	deactivate ArgumentMatchers

核心:参数匹配器被包装成LocalizedMatcher压入ArgumentMatcherStorageImpl管理的栈中。

2. 执行被存根方法

sequenceDiagram
	participant Test
	participant mock代理类
	participant DispatcherDefaultingToRealMethod
	participant Callable
	participant MockMethodInterceptor
	participant InvocationNotifierHandler
	participant NullResultGuardian
	participant MockHandlerImpl
	participant InvocationContainerImpl
	participant MockingProgressImpl
	participant StubbedInvocationMatcher
	participant Answers
	
	Test ->> mock代理类 : String testMethodString(String)
	mock代理类 ->> DispatcherDefaultingToRealMethod : Object interceptSuperCallable(<br/>Object,MockMethodInterceptor,<br/>Method,Object[],Callable<?> superCall)
	alt MockMethodInterceptor为空
		DispatcherDefaultingToRealMethod ->> Callable : V call()
		Callable -->> DispatcherDefaultingToRealMethod : 真实方法调用返回值
    else
    	DispatcherDefaultingToRealMethod ->> MockMethodInterceptor : Object doIntercept(Object, Method, Object[], RealMethod)
    	MockMethodInterceptor ->> MockMethodInterceptor : Object doIntercept(Object, Method, Object[], RealMethod,Location)
    	MockMethodInterceptor ->> InvocationNotifierHandler : Object handle(Invocation)
    	InvocationNotifierHandler ->> NullResultGuardian : Object handle(Invocation)
    	NullResultGuardian ->> MockHandlerImpl : Object handle(Invocation)
    	MockHandlerImpl ->> ArgumentMatcherStorageImpl : List<LocalizedMatcher> pullLocalizedMatchers()
    	ArgumentMatcherStorageImpl -->> MockHandlerImpl : 之前调用过的参数匹配器的封装类LocalizedMatcher列表
    	MockHandlerImpl ->> InvocationContainerImpl : void setInvocationForPotentialStubbing(<br/>MatchableInvocation)
    	Note over MockHandlerImpl,InvocationContainerImpl : 把包含调用信息的MatchableInvocation传入保存
    	InvocationContainerImpl -->> MockHandlerImpl : 
    	MockHandlerImpl ->> MockingProgressImpl : void reportOngoingStubbing(OngoingStubbing<?>)
    	Note over MockHandlerImpl,MockingProgressImpl : 生成OngoingStubbingImpl并在MockingProgress中记录
    	MockingProgressImpl -->> MockHandlerImpl : 
    	MockHandlerImpl ->> InvocationContainerImpl : StubbedInvocationMatcher findAnswerFor(Invocation)
    	Note over InvocationContainerImpl,InvocationContainerImpl : 从记录的存根中查找与调用匹配的并返回
    	InvocationContainerImpl -->> MockHandlerImpl : StubbedInvocationMatcher
    	alt 找到匹配的StubbedInvocationMatcher
            MockHandlerImpl ->> StubbedInvocationMatcher : Object answer(InvocationOnMock)
            StubbedInvocationMatcher -->> MockHandlerImpl : 预设的方法调用返回值
        else
            MockHandlerImpl ->> Answers : Object answer(InvocationOnMock invocation)
            Answers -->> MockHandlerImpl : 根据Answer策略返回默认的的方法调用返回值
        end
    	MockHandlerImpl -->> NullResultGuardian : 方法调用返回值
    	Note over NullResultGuardian,NullResultGuardian : 如果返回值是基本数据类型并且上游返回值为null,返回基本数据类型对应的默认值
    	NullResultGuardian -->> InvocationNotifierHandler : 方法调用返回值
    	activate InvocationNotifierHandler
    	InvocationNotifierHandler -->> InvocationNotifierHandler : void notifyMethodCall(Invocation, Object)
    	deactivate InvocationNotifierHandler
    	InvocationNotifierHandler -->> MockMethodInterceptor : 方法调用返回值
    	MockMethodInterceptor -->> DispatcherDefaultingToRealMethod : 方法调用返回值
    end
    DispatcherDefaultingToRealMethod -->> mock代理类 : 方法调用返回值
	mock代理类 -->> Test : 方法调用返回值

核心:

  1. 通过代理方法调用,最终将方法调用信息传递给MockMethodInterceptor处理,MockMethodInterceptor进一步把调用信息封装成Invocation实例交给MockHandler处理;
  2. MockHandler处理为责任链模式,默认情况下依次由InvocationNotifierHandlerNullResultGuardianMockHandlerImpl处理,核心逻辑在MockHandlerImpl
  3. MockHandlerImplInvocation信息封装到InvocationMatcher实例中,交给InvocationContainerImpl存储,这个会在设置预置值时使用;
  4. MockHandlerImplInvocationContainerImpl实例封装到OngoingStubbingImpl实例中,交给MockingProgressImpl存储,这个会在开启方法存根时使用;
  5. 尝试在已存在的存根中匹配可以处理该方法调用的存根,有的话用存根计算返回值返回,否则,根据Answer策略返回默认的的方法调用返回值;

3. 开启方法存根

sequenceDiagram
	participant Test
	participant Mockito
	participant MockitoCore
	participant MockingProgressImpl
	
	Test ->> Mockito : <T> OngoingStubbing<T> when(T methodCall)
	Mockito ->> MockitoCore : OngoingStubbing<T> when(T methodCall)
	MockitoCore ->> MockingProgressImpl : void stubbingStarted()
	MockingProgressImpl -->> MockitoCore : 方法调用返回值
	MockitoCore ->> MockingProgressImpl : OngoingStubbing<?> pullOngoingStubbing()
	MockingProgressImpl -->> MockitoCore : OngoingStubbing<?>
    MockitoCore -->> Mockito : OngoingStubbing<?>
    Mockito -->> Test : OngoingStubbing<?>

核心:

  1. MockHandlerImpl标记开始方法存根设置;
  2. 取出之前存储在MockHandlerImpl中的OngoingStubbingImpl,返回给上层;

4. 预置返回值

sequenceDiagram
	participant Test
	participant OngoingStubbingImpl
	participant InvocationContainerImpl
	participant MockingProgressImpl
	participant stubbed as LinkedList<StubbedInvocationMatcher>
	
	Test ->> OngoingStubbingImpl : OngoingStubbing<T> thenReturn(T value)
	activate OngoingStubbingImpl
	Note over OngoingStubbingImpl,OngoingStubbingImpl : 单纯的返回值被包装成Returns类型的Answer并调用thenAnswer方法
	OngoingStubbingImpl ->> OngoingStubbingImpl : OngoingStubbing<T> thenAnswer(Answer<?> answer)
	OngoingStubbingImpl ->> InvocationContainerImpl : boolean hasInvocationForPotentialStubbing()
	InvocationContainerImpl -->> OngoingStubbingImpl : 有没有潜在的存根的方法调用
	alt 没有潜在的存根的方法调用
		OngoingStubbingImpl ->> OngoingStubbingImpl : 抛出异常
    else
    	OngoingStubbingImpl ->> InvocationContainerImpl : void addAnswer(Answer, Strictness)
    	InvocationContainerImpl ->> MockingProgressImpl : void stubbingCompleted()
    	MockingProgressImpl -->> InvocationContainerImpl : 
    	InvocationContainerImpl ->> stubbed : void addFirst(StubbedInvocationMatcher)
    	Note over InvocationContainerImpl,stubbed : 把之前存储的InvocationMatcher和Answer<br/>包装成StubbedInvocationMatcher存储到链表中
    	stubbed -->> InvocationContainerImpl : 
    	InvocationContainerImpl -->> OngoingStubbingImpl : 
    end
	deactivate OngoingStubbingImpl
    OngoingStubbingImpl -->> Test : OngoingStubbing<?>

核心:

我们的预置值将会被封装成Answer,最终,之前存储的InvocationMatcherAnswer会被包装在StubbedInvocationMatcher中存储到链表。

三、调用

调用即执行被存根方法,因此与上面[2. 执行被存根方法](#2. 执行被存根方法)流程相同,当设置了存根时,InvocationContainerImplStubbedInvocationMatcher findAnswerFor(Invocation)方法会找到相应StubbedInvocationMatcherStubbedInvocationMatcher会计算出预置结果并返回。