前面分析过了Mockito是如何通过注解创建对应的mock对象,并对其所有方法设置拦截器。这篇博客再分析下Mockito是如何实现自动装配和插桩的,只有更好的了解其原理,在写复杂单测的时候才能更加得心应手。
@InjectMocks的自动装配
InjectingAnnotationEngine.java
public void process(Class<?> clazz, Object testInstance) {
//创建mock对象或者spy对象
processIndependentAnnotations(testInstance.getClass(), testInstance);
//对添加了@InjectMocks注解的对象进行自动装配
processInjectMocks(testInstance.getClass(), testInstance);
}
之前详细分析了processIndependentAnnotations(final Class<?> clazz, final Object testInstance源码,现在再看看processInjectMocks(final Class<?> clazz, final Object testInstance) 这部分源码
InjectingAnnotationEngine.java
private void processInjectMocks(final Class<?> clazz, final Object testInstance) {
Class<?> classContext = clazz;
//循环,从当前测试类对象开始执行自动注入的方法,一直到所有的非Object的父类都注入完成
while (classContext != Object.class) {
injectMocks(testInstance);
classContext = classContext.getSuperclass();
}
}
public void injectMocks(final Object testClassInstance) {
//获得当前测试类Class
Class<?> clazz = testClassInstance.getClass();
Set<Field> mockDependentFields = new HashSet<Field>();
Set<Object> mocks = newMockSafeHashSet();
while (clazz != Object.class) {
//[1]从当前测试类开始遍历找到添加了@InjectMock属性加入到mockDependentFields集合 (一直向上遍历,直到Object)
new InjectMocksScanner(clazz).addTo(mockDependentFields);
//从当前测试类开始遍历找到添加了@Mock属性加入到mocks集合
new MockScanner(testClassInstance, clazz).addPreparedMocks(mocks);
//这里是个钩子方法,没有实现
onInjection(testClassInstance, clazz, mockDependentFields, mocks);
clazz = clazz.getSuperclass();
}
//[2]真正对添加了@InjectMocks注解的对象进行自动注入
new DefaultInjectionEngine().injectMocksOnFields(mockDependentFields, mocks, testClassInstance);
}
[1]InjectMocksScanner和MockScanner
InjectMocksScanner.java就是通过反射拿到clazz的所有属性,将添加了@InjectMocks注解但是没有加@Mock和@Captor的属性都添加到mockDependentFields集合中 ,从源码可以看出 : 不能同时对一个属性添加@Mock和@InjectMocks注解,否则会抛出异常
MockScanner同理,就是拿到clazz下所有添加了@Spy或者@Mock注解的对象(也可以是通过api生成的mock或者spy对对象)
public InjectMocksScanner(Class<?> clazz) {
this.clazz = clazz;
}
public void addTo(Set<Field> mockDependentFields) {
//将遍历到的满足条件的属性都加入到参数集合中
mockDependentFields.addAll(scan());
}
private Set<Field> scan() {
Set<Field> mockDependentFields = new HashSet<Field>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//将添加了@InjectMocks并且没有加上@Mock和@Captor的属性都加入到集合中
if (null != field.getAnnotation(InjectMocks.class)) {
assertNoAnnotations(field, Mock.class, Captor.class);
mockDependentFields.add(field);
}
}
return mockDependentFields;
}
[2]injectMocksOnFields 自动注入
创建MockInjection对象,顺便指定注入策略(构造器注入和属性注入),以及注入后置处理器,完成对@InjectMocks属性的处理
DefaultInjectionEngine.java
private final MockInjectionStrategy injectionStrategies = MockInjectionStrategy.nop();
public void injectMocksOnFields(Set<Field> needingInjection, Set<Object> mocks, Object testClassInstance) {
//分别将needingInjection和testClassInstance赋值给OngoingMockInjection的fields和fieldOwner字段
MockInjection.onFields(needingInjection, testClassInstance)
//赋值给给OngoingMockInjection的mocks字段
.withMocks(mocks)
//向injectionStrategies注入策略添加构造器注入ConstructorInjection
.tryConstructorInjection()
//向injectionStrategies注入策略添加属性注入PropertyAndSetterInjection(这是一个责任链模式)
.tryPropertyOrFieldInjection()
//给postInjectionStrategies添加SpyOnInjectedFieldsHandler(将InjectsMock的属性采用spy方法初始化??)
.handleSpyAnnotation()
//执行属性注入和注入后的后置处理
.apply();
}
//MockInjection.java
public void apply() {
for (Field field : fields) {
injectionStrategies.process(field, fieldOwner, mocks);
postInjectionStrategies.process(field, fieldOwner, mocks);
}
}
//MockInjectionStrategy.java
public boolean process(Field onField, Object fieldOwnedBy, Set<Object> mockCandidates) {
//先执行构造器注入,如果失败则获取下一个策略--属性注入
if(processInjection(onField, fieldOwnedBy, mockCandidates)) {
return true;
}
return relayProcessToNextStrategy(onField, fieldOwnedBy, mockCandidates);
}
分别看下两个注入策略
ConstructorInjection.java
public boolean processInjection(Field field, Object fieldOwner, Set<Object> mockCandidates) {
try {
SimpleArgumentResolver simpleArgumentResolver = new SimpleArgumentResolver(mockCandidates);
//构建其FieldInitialization进行初始化(由于在测试类里没有带参数的构造函数,所以这里会走到异常)
FieldInitializationReport report = new FieldInitializer(fieldOwner, field, simpleArgumentResolver).initialize();
return report.fieldWasInitializedUsingContructorArgs();
} catch (MockitoException e) {
if(e.getCause() instanceof InvocationTargetException) {
Throwable realCause = e.getCause().getCause();
throw fieldInitialisationThrewException(field, realCause);
}
// other causes should be fine
return false;
}
}
PropertyAndSetterInjection.java
private final MockCandidateFilter mockCandidateFilter =
new TypeBasedCandidateFilter(
new NameBasedCandidateFilter(
new TerminalMockCandidateFilter()));
public boolean processInjection(Field injectMocksField, Object injectMocksFieldOwner, Set<Object> mockCandidates) {
//创建属性构造器
FieldInitializationReport report = initializeInjectMocksField(injectMocksField, injectMocksFieldOwner);
// for each field in the class hierarchy
boolean injectionOccurred = false;
//这里的fieldClass就是加了@InjectMocks注解的字段类型
Class<?> fieldClass = report.fieldClass();
//添加了@InjectMocks注解的字段的实例
Object fieldInstanceNeedingInjection = report.fieldInstance();
while (fieldClass != Object.class) {
injectionOccurred |= injectMockCandidates(fieldClass, fieldInstanceNeedingInjection, newMockSafeHashSet(mockCandidates));
fieldClass = fieldClass.getSuperclass();
}
return injectionOccurred;
}
private boolean injectMockCandidates(Class<?> awaitingInjectionClazz, Object injectee, Set<Object> mocks) {
boolean injectionOccurred;
//获取@InjectMocks对象的字段信息
List<Field> orderedCandidateInjecteeFields = orderedInstanceFieldsFrom(awaitingInjectionClazz);
//[1] 执行结果为true
injectionOccurred = injectMockCandidatesOnFields(mocks, injectee, false, orderedCandidateInjecteeFields);
//[2] 由于orderedCandidateInjecteeFields只有一个待被mock对象注入的属性,所以这一步没有执行什么逻辑,返回默认true,按位相或 也是true.
injectionOccurred |= injectMockCandidatesOnFields(mocks, injectee, injectionOccurred, orderedCandidateInjecteeFields);
return injectionOccurred;
}
//[1],[2]
private boolean injectMockCandidatesOnFields(Set<Object> mocks,
Object injectee,
boolean injectionOccurred,
List<Field> orderedCandidateInjecteeFields) {
//遍历被注入对象的属性
for (Iterator<Field> it = orderedCandidateInjecteeFields.iterator(); it.hasNext(); ) {
Field candidateField = it.next();
//匹配mock对象和待注入对象的属性
//TypeBasedCandidateFilter Class类型匹配,满足则进行下一步
//NameBasedCandidateFilter 判断mockName是否相同,是否没有同类型的其他字段需要注入 这里第一次[1]返回false
//TerminalMockCandidateFilter 对mock集合只有一个mock对象的情形进行注入
Object injected = mockCandidateFilter.filterCandidate(mocks, candidateField, orderedCandidateInjecteeFields, injectee)
.thenInject();
//mock对象被注入成功之后从mock对象集合中移除
if (injected != null) {
injectionOccurred |= true;
mocks.remove(injected);
it.remove();
}
}
return injectionOccurred;
}
TerminalMockCandidateFilter.java
public OngoingInjector filterCandidate(final Collection<Object> mocks,
final Field candidateFieldToBeInjected,
final List<Field> allRemainingCandidateFields,
final Object injectee) {
//集合里的mock对象只有1个
if(mocks.size() == 1) {
final Object matchingMock = mocks.iterator().next();
//返回一个匿名内部类的对象
return new OngoingInjector() {
public Object thenInject() {
try {
//这就是真正进行注入的方法,先通过属性的setter方法进行反射赋值;
//如果失败则通过反射直接对属性赋值
if (!new BeanPropertySetter(injectee, candidateFieldToBeInjected).set(matchingMock)) {
setField(injectee, candidateFieldToBeInjected,matchingMock);
}
} catch (RuntimeException e) {
throw cannotInjectDependency(candidateFieldToBeInjected, matchingMock, e);
}
return matchingMock;
}
};
}
return OngoingInjector.nop;
}
插桩Stubbing
之前简单分析过Mockito是怎么让mock对象调用方法都进入自己的拦截器,主要是在通过ByteBuddy生成mock代理类的时候定义了相关的拦截器
接下来再看看Mockito中最核心的Stubbing的实现
Mockito.when(userDao.queryUserByName(eq("chenpp"))).thenReturn(list);
Stub之When方法
先来看下When的作用,这里也是进入了MOCKITO_CORE的对应方法,其代码都是围绕mockingProgress这个对象展开的,可以看到这是从ThreadLocal里获取到的一个对象, 调用其pullOngoingStubbing方法后获得一个OngoingStubbing的返回对象,也就是这个方法的返回值。可以看到when方法的入参是mock对象的某个调用方法的返回值,那么来看看这个入参到底是什么呢?
之前一篇博客里提到过mock对象的所有方法调用都会被拦截,进入到MockHandlerImpl的handle方法,再来仔细看看这个类的源码
@CheckReturnValue
public static <T> OngoingStubbing<T> when(T methodCall) {
return MOCKITO_CORE.when(methodCall);
}
//MockitoCore
public <T> OngoingStubbing<T> when(T methodCall) {
MockingProgress mockingProgress = mockingProgress();
mockingProgress.stubbingStarted();
@SuppressWarnings("unchecked")
OngoingStubbing<T> stubbing = (OngoingStubbing<T>) mockingProgress.pullOngoingStubbing();
if (stubbing == null) {
mockingProgress.reset();
throw missingMethodInvocation();
}
return stubbing;
}
ThreadSafeMockingProgress.java
private static final ThreadLocal<MockingProgress> MOCKING_PROGRESS_PROVIDER = new ThreadLocal<MockingProgress>() {
@Override
protected MockingProgress initialValue() {
return new MockingProgressImpl();
}
};
public final static MockingProgress mockingProgress() {
return MOCKING_PROGRESS_PROVIDER.get();
}
这里的mockingProgress实际是一个MockingProgressImpl的实例对象,具体的属性有什么等到后面使用到的时候再看
MockingProgressImpl.java
private final ArgumentMatcherStorage argumentMatcherStorage = new ArgumentMatcherStorageImpl();
private OngoingStubbing<?> ongoingStubbing;
private Localized<VerificationMode> verificationMode;
private Location stubbingInProgress = null;
private VerificationStrategy verificationStrategy;
private final Set<MockitoListener> listeners = new LinkedHashSet<MockitoListener>();
public MockingProgressImpl() {
this.verificationStrategy = getDefaultVerificationStrategy();
}
public static VerificationStrategy getDefaultVerificationStrategy() {
return new VerificationStrategy() {
public VerificationMode maybeVerifyLazily(VerificationMode mode) {
return mode;
}
};
}
MockHandlerImpl 拦截器进入的类
这里的Invocation即InterceptedInvocation,把代理类拦截时的方法调用和参数封装在一起,它包含以下几个对象:真正的方法realMethod,Mockito的方法MockitoMethod,参数arguments,以及mock对象mockRef等
public Object handle(Invocation invocation) throws Throwable {
//判断当前mock对象是否已插桩(在执行when的时候,还没有被插桩所有不会执行该分支)
if (invocationContainer.hasAnswersForStubbing()) {
// stubbing voids with doThrow() or doAnswer() style
InvocationMatcher invocationMatcher = matchersBinder.bindMatchers(
mockingProgress().getArgumentMatcherStorage(),
invocation
);
invocationContainer.setMethodForStubbing(invocationMatcher);
return null;
}
//获取verify信息
VerificationMode verificationMode = mockingProgress().pullVerificationMode();
//invocationMatcher里包含invocation信息以及参数匹配信息(bindMatchers执行之后会将之前的信息返回并且清空ArgumentMatcherStorageImpl存储匹配信息的matcherStack)
InvocationMatcher invocationMatcher = matchersBinder.bindMatchers(
//ArgumentMatcherStorageImpl的实例对象,保存参数匹配的相关信息
mockingProgress().getArgumentMatcherStorage(),
invocation
);
//验证当前MockingProgressImpl状态是否正常,比方说matcherStack是否已经被重置清理等
mockingProgress().validateState();
// verificationMode 只有当有人在操作verify的是才会有值,插桩和mock的是都是null
if (verificationMode != null) {
// 判断verification的mock对象和在使用的mock对象是否是一个
if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) {
VerificationDataImpl data = new VerificationDataImpl(invocationContainer, invocationMatcher);
verificationMode.verify(data);
return null;
} else {
// 当前的invocation是另一个mock对象的,将verify信息重新加回去
//this means there is an invocation on a different mock. Re-adding verification mode
mockingProgress().verificationStarted(verificationMode);
}
}
// prepare invocation for stubbing
invocationContainer.setInvocationForPotentialStubbing(invocationMatcher);
OngoingStubbingImpl<T> ongoingStubbing = new OngoingStubbingImpl<T>(invocationContainer);
//将ongoingStubbing赋值给MockingProgressImpl对象的ongoingStubbing属性,每次调用都会获取到最新的ongoingStubbing
mockingProgress().reportOngoingStubbing(ongoingStubbing);
//根据invocation查找对应的answer(在插桩的时候stubbed是空的)
// look for existing answer for this invocation
StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation);
// 通知回调,通知listener when completed, we should be able to get rid of the casting below
notifyStubbedAnswerLookup(invocation, stubbing, invocationContainer.getStubbingsAscending(),
(CreationSettings) mockSettings);
//插桩时stubbing为null
if (stubbing != null) {
stubbing.captureArgumentsFrom(invocation);
try {
return stubbing.answer(invocation);
} finally {
//Needed so that we correctly isolate stubbings in some scenarios
//see MockitoStubbedCallInAnswerTest or issue #1279
mockingProgress().reportOngoingStubbing(ongoingStubbing);
}
} else {
//获取默认的answer信息
Object ret = mockSettings.getDefaultAnswer().answer(invocation);
//验证answer类型和对应的方法返回值是否匹配
DefaultAnswerValidator.validateReturnValueFor(invocation, ret);
//Mockito uses it to redo setting invocation for potential stubbing in case of partial mocks / spies.
//为下一次可能的stub重置invocation (将invocationMatcher赋值给InvocationContainerImpl的invocationForStubbing属性)
invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher);
return ret;
}
}
再看下之前when里的源码就很清楚了,通过when(mockObj.methdo())方法调用,我们可以拿到在MockHandlerImpl创建的ongoingStubbing对象,里面包含invocation和我们调用方法的参数匹配信息
//MockitoCore
public <T> OngoingStubbing<T> when(T methodCall) {
//获取MockingProgressImpl实例对象
MockingProgress mockingProgress = mockingProgress();
//验证mockingProgress状态并且给stubbingInProgress属性赋值(即标记开始插桩)
mockingProgress.stubbingStarted();
@SuppressWarnings("unchecked")
//获取ongoingStubbing对象,这是在调用mock对象的方法时赋值的,包含invocation信息以及参数匹配信息
OngoingStubbing<T> stubbing = (OngoingStubbing<T>) mockingProgress.pullOngoingStubbing();
if (stubbing == null) {
mockingProgress.reset();
throw missingMethodInvocation();
}
return stubbing;
}
Stub之ThenReturn
将返回结果包装成answer,通过InvocationContainerImpl的addAnswer方法将stub和返回绑定
public OngoingStubbing<T> thenReturn(T value) {
return thenAnswer(new Returns(value));
}
private final InvocationContainerImpl invocationContainer;
@Override
public OngoingStubbing<T> thenAnswer(Answer<?> answer) {
if(!invocationContainer.hasInvocationForPotentialStubbing()) {
throw incorrectUseOfApi();
}
//向invocationContainer添加answer(这里的answer就是Return对象,包含我们创建的返回值对象)
invocationContainer.addAnswer(answer, strictness);
return new ConsecutiveStubbing<T>(invocationContainer);
}
InvocationContainerImpl.java
public void addAnswer(Answer answer, Strictness stubbingStrictness) {
registeredInvocations.removeLast();
addAnswer(answer, false, stubbingStrictness);
}
/**
* Adds new stubbed answer and returns the invocation matcher the answer was added to.
* @param isConsecutive连续不断的
*/
public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive, Strictness stubbingStrictness) {
Invocation invocation = invocationForStubbing.getInvocation();
//标记完成插桩
mockingProgress().stubbingCompleted();
if (answer instanceof ValidableAnswer) {
//验证设定的返回值是否正确
((ValidableAnswer) answer).validateFor(invocation);
}
synchronized (stubbed) {
//这里就是把调用和返回绑定,如果下次调用匹配到了,就返回对应的answer
if (isConsecutive) {
stubbed.getFirst().addAnswer(answer);
} else {
//这里设置的answer并不是连续的,所以直接加到stubbed(LinkedList)的头部就行
Strictness effectiveStrictness = stubbingStrictness != null ? stubbingStrictness : this.mockStrictness;
//这里的invocationForStubbing
stubbed.addFirst(new StubbedInvocationMatcher(answer, invocationForStubbing, effectiveStrictness));
}
return stubbed.getFirst();
}
}
这样在单元测试调用Mock对象的方法时,会再次进入MockHandlerImpl的handle方法
public StubbedInvocationMatcher findAnswerFor(Invocation invocation) {
synchronized (stubbed) {
for (StubbedInvocationMatcher s : stubbed) {
//匹配mock对象,方法信息,参数信息,如果都匹配上则说明是这个answer
if (s.matches(invocation)) {
s.markStubUsed(invocation);
//标记对应的stub信息 we should mark stubbed at the point of stubbing, not at the point where the stub is being used
invocation.markStubbed(new StubInfoImpl(s));
return s;
}
}
}
return null;
}
@Override
public boolean matches(Invocation candidate) {
return invocation.getMock().equals(candidate.getMock()) && hasSameMethod(candidate) && argumentsMatch(candidate);
}
最后执行StubbedInvocationMatcher的answer方法获取最终的返回结果,如下:
public Object answer(InvocationOnMock invocation) throws Throwable {
//see ThreadsShareGenerouslyStubbedMockTest
Answer a;
synchronized(answers) {
//这里是从answers中获取其中一个,如果只有一个则使用peek(),否则使用poll
//peek表示获取队列的队头元素但是并不移除,poll表示获取并移除队头元素(这是为了可以进行多次mock以及设置相同参数的每次不同的返回值--不了解的可以看下我之前关于Mockito使用的博客)
a = answers.size() == 1 ? answers.peek() : answers.poll();
}
//就是返回Returns封装的value值
return a.answer(invocation);
}
Verify
最后再简单讲下verify的原理,Mockito.verify(userDao, Mockito.times(1))返回的还是一开始的mock对象,所以后续方法调用依旧会走到MockHandlerImpl的handle方法,此时VerificationMode就不为null了
public <T> T verify(T mock, VerificationMode mode) {
if (mock == null) {
throw nullPassedToVerify();
}
//获取mock详细信息
MockingDetails mockingDetails = mockingDetails(mock);
if (!mockingDetails.isMock()) {
throw notAMockPassedToVerify(mock.getClass());
}
assertNotStubOnlyMock(mock);
//拿到mockHandler
MockHandler handler = mockingDetails.getMockHandler();
//调用VerificationStarted的通知回调
mock = (T) VerificationStartedNotifier.notifyVerificationStarted(
handler.getMockSettings().getVerificationStartedListeners(), mockingDetails);
MockingProgress mockingProgress = mockingProgress();
VerificationMode actualMode = mockingProgress.maybeVerifyLazily(mode);
//封装MockAwareVerificationMode对象并赋值给MockingProgressImpl的verificationMode字段属性
mockingProgress.verificationStarted(new MockAwareVerificationMode(mock, actualMode, mockingProgress.verificationListeners()));
return mock;
}
当VerificationMode不为空时,先验证mode的状态是否正确,验证通过后获取到对应的container和invocationMatcher信息,最后调用verificationMode的verify方法,完成验证。
if (verificationMode != null) {
// We need to check if verification was started on the correct mock
// - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) {
//获取校验信息,然后验证
VerificationDataImpl data = new VerificationDataImpl(invocationContainer, invocationMatcher);
verificationMode.verify(data);
return null;
} else {
// this means there is an invocation on a different mock. Re-adding verification mode
// - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
mockingProgress().verificationStarted(verificationMode);
}
}
至此,Mockito主要的原理分析都完成了