一、创建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步:
- 创建一个
MockHandler实例, 这是Mockito的核心,负责处理对mock对象的调用; - 使用
ByteBuddy创建被mock的类的子类,并将MockHandler织入,代理原方法调用,随后使用Objenesis框架跳过构造创建该类的实例; - 将第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部分:
T ArgumentMatchers::eq(T)执行参数匹配器TestMockitoClass::testMethodString(String)执行被存根方法OngoingStubbing<T> Mockito::when(T)开启方法存根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 : 方法调用返回值
核心:
- 通过代理方法调用,最终将方法调用信息传递给
MockMethodInterceptor处理,MockMethodInterceptor进一步把调用信息封装成Invocation实例交给MockHandler处理; MockHandler处理为责任链模式,默认情况下依次由InvocationNotifierHandler、NullResultGuardian、MockHandlerImpl处理,核心逻辑在MockHandlerImpl;MockHandlerImpl将Invocation信息封装到InvocationMatcher实例中,交给InvocationContainerImpl存储,这个会在设置预置值时使用;MockHandlerImpl将InvocationContainerImpl实例封装到OngoingStubbingImpl实例中,交给MockingProgressImpl存储,这个会在开启方法存根时使用;- 尝试在已存在的存根中匹配可以处理该方法调用的存根,有的话用存根计算返回值返回,否则,根据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<?>
核心:
- 在
MockHandlerImpl标记开始方法存根设置; - 取出之前存储在
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,最终,之前存储的InvocationMatcher和Answer会被包装在StubbedInvocationMatcher中存储到链表。
三、调用
调用即执行被存根方法,因此与上面[2. 执行被存根方法](#2. 执行被存根方法)流程相同,当设置了存根时,InvocationContainerImpl的StubbedInvocationMatcher findAnswerFor(Invocation)方法会找到相应StubbedInvocationMatcher,StubbedInvocationMatcher会计算出预置结果并返回。