Mockito使用示例
在开始看mockito的源码之前,首先看一下下面两个mockito的使用示例。
简单使用示例
@Test
public void mockitoTest(){
// 1.生成一个mock对象
List<String> mockedList = Mockito.mock(List.class);
// 查看mock对象的类名: $java.util.List$$EnhancerByMockitoWithCGLIB$$6c214c3e
System.out.println(mockedList.getClass().getName());
// 2.操作mock对象
mockedList.add("one");
mockedList.get(0);
mockedList.clear();
// 3.verify验证,mock对象是否发生过某些行为,如果验证不通过,Mockito会抛出异常
Mockito.verify(mockedList).add("one");
Mockito.verify(mockedList).get(0);
// 4.stub打桩,指定mock对象某些行为的返回,也就是我们常用的mock数据
Mockito.when(mockedList.get(1)).thenReturn("13");
// 5.这里打印出“13”(但我们知道mockedList实际是没有下标为1的元素,这就是mock的功能)
Assert.assertEquals("13", mockedList.get(1));
}
注解使用示例
Mockito中提供了一些常用的注解来实现Mockito静态类中的方法功能,例如@Mock,@Spy,@InjectMocks,@Rule等,下面是简单的示例:
@RunWith(MockitoRunner.class)
public class DemoTest {
@InjectMocks
private UserServiceImpl userService;
@Mock
private UserManager userManager;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void mockitoTest() {
// mock打桩和测试方法调用(示例方法,无法运行)
Mockito.when(userManager.selectAll(any())).thenReturn(MockBeanFactory.mockUserList());
Mockito.verify(userManager, Mockito.times(0)).selectAll(any());
userService.selectAll(MockBeanFactory.newUserQuery());
Mockito.verify(userManager, Mockito.times(1)).selectAll(any());
Mockito.when(userManager.selectAll(any())).thenThrow(new RuntimeException());
userService.selectAll(MockBeanFactory.newUserQuery());
Mockito.verify(userManager, Mockito.times(2)).selectAll(any());
}
Mockito类相当于整个框架的门面,负责对外提供调用接口,常用的静态方法包括:mock、spy、when、thenReturn、verify等。
在实现上,Mock使用了代理模式。上述操作的list,实际上是继承了List
的代理类的实例,我们把它称为 proxy 对象。因此对于list 的所有操作,都会先经过 proxy 对象。
Mocktio 就是通过这种方式,拦截到了list 的所有操作。只要在调用list 的方法时,将该方法存放起来,然后在调用 thenReturn 为其设置返回值,来返回我们设置的mock结果。
相关源码分析
Mockito源码版本:3.X
Github地址:https://github.com/mockito/mockito,可以将项目代码导入IDEA中查看源码。注意mockito使用的是gradle编译工具,在导入IDEA时需要选用该工具,而不是maven。
MockitoAnnotations.initMocks
该方法试图加载全局的配置信息,并且预留了扩展点,允许自己定制 Mock 对象的默认返回值以及处理注解的引擎、扩展插件等(但一般情况下,均可以保持默认)。
具体的扩展点代码在如下类中可以找到:
org.mockito.internal.configuration.plugins.PluginRegistry
org.mockito.internal.configuration.plugins.PluginLoader#loadImpl
org.mockito.internal.configuration.ClassPathLoader#loadConfiguration
MockitoAnnotations.initMocks方法源码如下:
/**
* Initializes objects annotated with Mockito annotations for given testClass:
* @{@link org.mockito.Mock}, @{@link Spy}, @{@link Captor}, @{@link InjectMocks}
* <p>
* See examples in javadoc for {@link MockitoAnnotations} class.
*/
public static void initMocks(Object testClass) {
if (testClass == null) {
throw new MockitoException("testClass cannot be null. For info how to use @Mock annotations see examples in javadoc for MockitoAnnotations class");
}
// 1.获取默认注解处理引擎
AnnotationEngine annotationEngine = new GlobalConfiguration().getAnnotationEngine();
Class<?> clazz = testClass.getClass();
// 2.省略用户自定义注解处理
//anyway act 'the new' way
// 3.使用注解处理引擎处理测试类
annotationEngine.process(testClass.getClass(), testClass);
}
获取默认注解处理引擎时,一般情况拿到的是InjectingAnnotationEngine类的对象,然后在process方法中分处理注解和注入关系。
InjectingAnnotationEngine类
InjectingAnnotationEngine类的process方法主要包含下面两个子方法:
/**
* Process the fields of the test instance and create Mocks, Spies, Captors and inject them on fields
* annotated @InjectMocks.
*
* <p>
* This code process the test class and the super classes.
* <ol>
* <li>First create Mocks, Spies, Captors.</li>
* <li>Then try to inject them.</li>
* </ol>
*
* @param clazz Not used
* @param testInstance The instance of the test, should not be null.
*
* @see org.mockito.configuration.AnnotationEngine#process(Class, Object)
*/
public void process(Class<?> clazz, Object testInstance) {
// 1.处理独立的注解,包括@Mock,@Spy,@Captor
processIndependentAnnotations(testInstance.getClass(), testInstance);
// 2.扫描当前测试类,使用DI将注解标识的对象注入到@InjectMock标识的类对象的Field中
processInjectMocks(testInstance.getClass(), testInstance);
}
processIndependentAnnotations方法
processIndependentAnnotations方法将不同的注解处理逻辑,交给具体的注解处理引擎进行处理。
private void processIndependentAnnotations(final Class<?> clazz, final Object testInstance) {
Class<?> classContext = clazz;
while (classContext != Object.class) {
// 1.分别加载MockAnnotationProcessor和CaptorAnnotationProcessor处理@Mock和@Captor注解
//this will create @Mocks, @Captors, etc:
delegate.process(classContext, testInstance);
// 2.加载spy处理引擎处理@Spy注解
//this will create @Spies:
spyAnnotationEngine.process(classContext, testInstance);
classContext = classContext.getSuperclass();
}
}
process方法
- @mock和@captor注解处理delegate.process方法,使用DefaultAnnotationEngine对象进行处理,主要步骤如下:
public void process(Class<?> clazz, Object testInstance) {
Field[] fields = clazz.getDeclaredFields();
// 1.遍历类的所有Field属性
for (Field field : fields) {
boolean alreadyAssigned = false;
// 2.获取每个Field属性的注解
for(Annotation annotation : field.getAnnotations()) {
// 3.针对不同的注解,创建mock对象
Object mock = createMockFor(annotation, field);
if (mock != null) {
throwIfAlreadyAssigned(field, alreadyAssigned);
alreadyAssigned = true;
try {
// 4.将mock对象set到测试对象中
new FieldSetter(testInstance, field).set(mock);
} catch (Exception e) {
throw new MockitoException("Problems setting field " + field.getName() + " annotated with "
+ annotation, e);
}
}
}
}
}
createMockFor方法
- 其中创建mock对象的方法createMockFor如下(可以看到这里处理的是@Mock和@Captor注解):
/* (non-Javadoc)
* @see org.mockito.AnnotationEngine#createMockFor(java.lang.annotation.Annotation, java.lang.reflect.Field)
*/
@SuppressWarnings("deprecation")
public Object createMockFor(Annotation annotation, Field field) {
// 1.处理@Mock注解,创建Mock对象
if (annotation instanceof Mock) {
return processAnnotationOn((Mock) annotation, field);
}
if (annotation instanceof MockitoAnnotations.Mock) {
return processAnnotationOn((MockitoAnnotations.Mock) annotation, field);
}
// 2.处理@Captor注解,创建对象
if (annotation instanceof Captor) {
return processAnnotationOn((Captor) annotation, field);
}
return null;
}
processAnnotationOn方法
- 其中processAnnotationOn方法如下:
主要包含两个步骤:
- 创建MockSettings对象
- 调用Mockito.mock方法创建mock对象
private Object processAnnotationOn(Mock annotation, Field field) {
// 1.创建MockSettings对象
MockSettings mockSettings = Mockito.withSettings();
if (annotation.extraInterfaces().length > 0) { // never null
mockSettings.extraInterfaces(annotation.extraInterfaces());
}
if ("".equals(annotation.name())) {
mockSettings.name(field.getName());
} else {
mockSettings.name(annotation.name());
}
// see @Mock answer default value
mockSettings.defaultAnswer(annotation.answer().get());
// 2.调用MockitoCore创建mock对象
return Mockito.mock(field.getType(), mockSettings);
}
可以看到,这里创建mock对象时,调用的也是Mockito.mock方法进行创建的,这部分在后续的mock对象的创建过程中进行分析。首先可以看一下,该方法的底层使用的是MOCKITO_CORE来进行mock对象的创建。
Mockito.mock方法
/**
* Creates a mock with some non-standard settings.
* <p>
* The number of configuration points for a mock grows
* so we need a fluent way to introduce new configuration without adding more and more overloaded Mockito.mock() methods.
* Hence {@link MockSettings}.
* <pre class="code"><code class="java">
* Listener mock = mock(Listener.class, withSettings()
* .name("firstListner").defaultBehavior(RETURNS_SMART_NULLS));
* );
* </code></pre>
* <b>Use it carefully and occasionally</b>. What might be reason your test needs non-standard mocks?
* Is the code under test so complicated that it requires non-standard mocks?
* Wouldn't you prefer to refactor the code under test so it is testable in a simple way?
* <p>
* See also {@link Mockito#withSettings()}
* <p>
* See examples in javadoc for {@link Mockito} class
*
* @param classToMock class or interface to mock
* @param mockSettings additional mock settings
* @return mock object
*/
public static <T> T mock(Class<T> classToMock, MockSettings mockSettings) {
return MOCKITO_CORE.mock(classToMock, mockSettings);
}
processInjectMocks方法
processInjectMocksf方法主要用来实现依赖注入:
private void processInjectMocks(final Class<?> clazz, final Object testInstance) {
Class<?> classContext = clazz;
// 向上遍历父类,依次进行对象的注入
while (classContext != Object.class) {
injectMocks(testInstance);
classContext = classContext.getSuperclass();
}
}
injectMocks方法
/**
* Initializes mock/spies dependencies for objects annotated with
* @InjectMocks for given testClass.
* <p>
* See examples in javadoc for {@link MockitoAnnotations} class.
*
* @param testClass
* Test class, usually <code>this</code>
*/
public void injectMocks(final Object testClass) {
Class<?> clazz = testClass.getClass();
Set<Field> mockDependentFields = new HashSet<Field>();
Set<Object> mocks = new HashSet<Object>();
while (clazz != Object.class) {
// 1.扫描当前测试类,获取所有@InjectMocks注解标识的属性
mockDependentFields.addAll(scanForInjection(testClass, clazz));
// 2.扫描当前测视类,获取所有@Mock和@Spy注解标识的属性
mocks.addAll(scanMocks(testClass, clazz));
clazz = clazz.getSuperclass();
}
// 3.将@Mock和@Spy标识的属性注入到@InjectMocks标识的属性中
new DefaultInjectionEngine().injectMocksOnFields(mockDependentFields, mocks, testClass);
}
scanForInjection方法
主要用来扫描测试类中,具有@InjectMocks注解的属性:
/**
* Scan fields annotated by @InjectMocks
*
* @param testClass
* @param clazz
* @return
*/
private Set<Field> scanForInjection(final Object testClass, final Class<?> clazz) {
Set<Field> mockDependentFields = new HashSet<Field>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (null != field.getAnnotation(InjectMocks.class)) {
assertNoAnnotations(field, Mock.class, MockitoAnnotations.Mock.class, Captor.class);
mockDependentFields.add(field);
}
}
return mockDependentFields;
}
scanMocks方法
主要用于扫描@Mock和@Spy标注的属性:
private Set<Object> scanMocks(final Object testClass, final Class<?> clazz) {
Set<Object> mocks = new HashSet<Object>();
for (Field field : clazz.getDeclaredFields()) {
// mock or spies only
if (null != field.getAnnotation(Spy.class) || null != field.getAnnotation(org.mockito.Mock.class)
|| null != field.getAnnotation(org.mockito.MockitoAnnotations.Mock.class)) {
Object fieldInstance = null;
boolean wasAccessible = field.isAccessible();
field.setAccessible(true);
try {
fieldInstance = field.get(testClass);
} catch (IllegalAccessException e) {
throw new MockitoException("Problems reading this field dependency " + field.getName() + " for injection", e);
} finally {
field.setAccessible(wasAccessible);
}
if (fieldInstance != null) {
mocks.add(fieldInstance);
}
}
}
return mocks;
}
injectMocksOnFields方法
将上述扫描的结果,进行依赖注入,装配所有@InjectMocks标识的属性对象:
public void injectMocksOnFields(Set<Field> needingInjection, Set<Object> mocks, Object testClassInstance) {
MockInjection.onFields(needingInjection, testClassInstance)
.withMocks(mocks)
.tryConstructorInjection()
.tryPropertyOrFieldInjection()
.handleSpyAnnotation()
.apply();
}
mock
根据Mockito的简单使用示例,生成 Mock 对象可以直接调用mock静态方法,也可以通过@Mock注解来实现:
List mock = Mockito.mock(List.class);
@Mock
List mock;
MOCKITO_CORE.mock
无论使用 @Mock
注解还是手动调用 mock
方法,最终都会转交给 MOCKITO_CORE
来完成。
/**
* Creates mock object of given class or interface.
* <p>
* See examples in javadoc for {@link Mockito} class
*
* @param classToMock class or interface to mock
* @return mock object
*/
public static <T> T mock(Class<T> classToMock) {
return mock(classToMock, withSettings().defaultAnswer(RETURNS_DEFAULTS));
}
mock方法默认使用的MockSettingImpl对象中设置了默认的RETURNS_DEFAULTS,这里的RETURNS_DEFAULTS对象实际上是:
public enum Answers implements Answer<Object> {
/**
* The default configured answer of every mock.
*
* <p>Please see the {@link org.mockito.Mockito#RETURNS_DEFAULTS} documentation for more details.</p>
*
* @see org.mockito.Mockito#RETURNS_DEFAULTS
*/
RETURNS_DEFAULTS(new GloballyConfiguredAnswer()),
// 省略其他
}
上述的mock方法实际调用的都是:
/**
* Creates a mock with some non-standard settings.
* <p>
* The number of configuration points for a mock grows
* so we need a fluent way to introduce new configuration without adding more and more overloaded Mockito.mock() methods.
* Hence {@link MockSettings}.
* <pre class="code"><code class="java">
* Listener mock = mock(Listener.class, withSettings()
* .name("firstListner").defaultBehavior(RETURNS_SMART_NULLS));
* );
* </code></pre>
* <b>Use it carefully and occasionally</b>. What might be reason your test needs non-standard mocks?
* Is the code under test so complicated that it requires non-standard mocks?
* Wouldn't you prefer to refactor the code under test so it is testable in a simple way?
* <p>
* See also {@link Mockito#withSettings()}
* <p>
* See examples in javadoc for {@link Mockito} class
*
* @param classToMock class or interface to mock
* @param mockSettings additional mock settings
* @return mock object
*/
public static <T> T mock(Class<T> classToMock, MockSettings mockSettings) {
return MOCKITO_CORE.mock(classToMock, mockSettings);
}
其中 MockSettings
的实现类 MockSettingsImpl
和 CreationSettings
承载着相关配置信息,比如要为待 mock 的对象生成怎样的返回值:
public <T> T mock(Class<T> typeToMock, MockSettings settings) {
if (!MockSettingsImpl.class.isInstance(settings)) {
throw new IllegalArgumentException(
"Unexpected implementation of '"
+ settings.getClass().getCanonicalName()
+ "'\n"
+ "At the moment, you cannot provide your own implementations of that class.");
}
// 1.创建mock的相关配置对象MockSettings和CreationSettings对象
MockSettingsImpl impl = MockSettingsImpl.class.cast(settings);
MockCreationSettings<T> creationSettings = impl.build(typeToMock);
// 2.根据mock配置对象创建实际的mock对象
T mock = createMock(creationSettings);
// 3.执行mock的过程
mockingProgress().mockingStarted(mock, creationSettings);
return mock;
}
MockSettingsImpl.build方法
在使用MockSettingsImpl对象调用impl.build(typeToMock)方法创建MockCreationSettings对象时,会先进行前置校验,出错时抛出异常:
@Override
public <T> MockCreationSettings<T> build(Class<T> typeToMock) {
return validatedSettings(typeToMock, (CreationSettings<T>) this);
}
MockUtil.createMock方法
接着,在实际的createMock方法中,会转交给MockUtil执行。这里包了很多层,但是handle最终是调用MockHandlerImpl的handle方法(MockHandlerImpl是核心中核心类)。
public static <T> T createMock(MockCreationSettings<T> settings) {
// 1.创建MockHandler对象
MockHandler mockHandler = createMockHandler(settings);
Object spiedInstance = settings.getSpiedInstance();
T mock;
if (spiedInstance != null) {
mock =
mockMaker
.createSpy(settings, mockHandler, (T) spiedInstance)
.orElseGet(
() -> {
T instance = mockMaker.createMock(settings, mockHandler);
new LenientCopyTool().copyToMock(spiedInstance, instance);
return instance;
});
} else {
// 2.调用ByteBuddy完成代理类字节码运行时动态生成
mock = mockMaker.createMock(settings, mockHandler);
}
return mock;
}
createMockHandler是通过MockHandlerFactory方法返回handler对象:
public static <T> MockHandler<T> createMockHandler(MockCreationSettings<T> settings) {
MockHandler<T> handler = new MockHandlerImpl<T>(settings);
MockHandler<T> nullResultGuardian = new NullResultGuardian<T>(handler);
return new InvocationNotifierHandler<T>(nullResultGuardian, settings);
}
ByteBuddyMockMaker.createMock方法
createMock方法的主要作用,是代理类的创建:
@Override
public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
// 1.创建代理类
Class<? extends T> mockedProxyType = createMockType(settings);
Instantiator instantiator = Plugins.getInstantiatorProvider().getInstantiator(settings);
T mockInstance = null;
try {
// 2.创建代理类实例
mockInstance = instantiator.newInstance(mockedProxyType);
MockAccess mockAccess = (MockAccess) mockInstance;
mockAccess.setMockitoInterceptor(new MockMethodInterceptor(handler, settings));
return ensureMockIsAssignableToMockedType(settings, mockInstance);
} catch (ClassCastException cce) {
throw new MockitoException(
join(
"ClassCastException occurred while creating the mockito mock :",
" class to mock : " + describeClass(settings.getTypeToMock()),
" created class : " + describeClass(mockedProxyType),
" proxy instance class : " + describeClass(mockInstance),
" instance creation by : " + instantiator.getClass().getSimpleName(),
"",
"You might experience classloading issues, please ask the mockito mailing-list.",
""),
cce);
} catch (org.mockito.creation.instance.InstantiationException e) {
throw new MockitoException(
"Unable to create mock instance of type '"
+ mockedProxyType.getSuperclass().getSimpleName()
+ "'",
e);
}
}
SubclassByteBuddyMockMaker.createMockType方法
实际调用的是org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator#mockClass,之后进行拦截器的配置(mock创建的代理对象的方法调用,都会被这里设置的interpretor拦截器拦截,来执行thenReturn模拟的返回结果)等,到这里就算完成了代理类替换真是类的主要过程。
@Override
public <T> Class<? extends T> createMockType(MockCreationSettings<T> settings) {
try {
return cachingMockBytecodeGenerator.mockClass(
MockFeatures.withMockFeatures(
settings.getTypeToMock(),
settings.getExtraInterfaces(),
settings.getSerializableMode(),
settings.isStripAnnotations()));
} catch (Exception bytecodeGenerationFailed) {
throw prettifyFailure(settings, bytecodeGenerationFailed);
}
}
SubclassBytecodeGenerator.mockClass方法
这里是生成代理类的关键代码。Mockito使用的是ByteBuddy这个框架,它并不需要编译器的帮助,而是直接生成class,然后使用ClassLoader来进行加载,感兴趣的可以深入研究,其地址为:https://github.com/raphw/byte-buddy,简单入门介绍可见:https://zhuanlan.zhihu.com/p/151843984。
@Override
public <T> Class<? extends T> mockClass(MockFeatures<T> features) {
ClassLoader classLoader =
new MultipleParentClassLoader.Builder()
.appendMostSpecific(getAllTypes(features.mockedType))
.appendMostSpecific(features.interfaces)
.appendMostSpecific(currentThread().getContextClassLoader())
.appendMostSpecific(MockAccess.class)
.build();
// If Mockito does not need to create a new class loader and if a mock is not based on a JDK
// type, we attempt
// to define the mock class in the user runtime package to allow for mocking package private
// types and methods.
// This also requires that we are able to access the package of the mocked class either by
// override or explicit
// privilege given by the target package being opened to Mockito.
boolean localMock =
classLoader == features.mockedType.getClassLoader()
&& features.serializableMode != SerializableMode.ACROSS_CLASSLOADERS
&& !isComingFromJDK(features.mockedType)
&& (loader.isDisrespectingOpenness()
|| handler.isOpened(features.mockedType, MockAccess.class));
String typeName;
if (localMock
|| loader instanceof MultipleParentClassLoader
&& !isComingFromJDK(features.mockedType)) {
typeName = features.mockedType.getName();
} else {
typeName =
InjectionBase.class.getPackage().getName()
+ "."
+ features.mockedType.getSimpleName();
}
String name =
String.format("%s$%s$%d", typeName, "MockitoMock", Math.abs(random.nextInt()));
if (localMock) {
handler.adjustModuleGraph(features.mockedType, MockAccess.class, false, true);
for (Class<?> iFace : features.interfaces) {
handler.adjustModuleGraph(iFace, features.mockedType, true, false);
handler.adjustModuleGraph(features.mockedType, iFace, false, true);
}
} else {
boolean exported = handler.isExported(features.mockedType);
Iterator<Class<?>> it = features.interfaces.iterator();
while (exported && it.hasNext()) {
exported = handler.isExported(it.next());
}
// We check if all mocked types are exported without qualification to avoid generating a
// hook type.
// unless this is necessary. We expect this to be the case for most mocked types what
// makes this a
// worthy performance optimization.
if (exported) {
assertVisibility(features.mockedType);
for (Class<?> iFace : features.interfaces) {
assertVisibility(iFace);
}
} else {
Class<?> hook = handler.injectionBase(classLoader, typeName);
assertVisibility(features.mockedType);
handler.adjustModuleGraph(features.mockedType, hook, true, false);
for (Class<?> iFace : features.interfaces) {
assertVisibility(iFace);
handler.adjustModuleGraph(iFace, hook, true, false);
}
}
}
DynamicType.Builder<T> builder =
byteBuddy
.subclass(features.mockedType)
.name(name)
.ignoreAlso(isGroovyMethod())
.annotateType(
features.stripAnnotations
? new Annotation[0]
: features.mockedType.getAnnotations())
.implement(new ArrayList<Type>(features.interfaces))
.method(matcher)
.intercept(dispatcher)
.transform(withModifiers(SynchronizationState.PLAIN))
.attribute(
features.stripAnnotations
? MethodAttributeAppender.NoOp.INSTANCE
: INCLUDING_RECEIVER)
.method(isHashCode())
.intercept(hashCode)
.method(isEquals())
.intercept(equals)
.serialVersionUid(42L)
.defineField("mockitoInterceptor", MockMethodInterceptor.class, PRIVATE)
.implement(MockAccess.class)
.intercept(FieldAccessor.ofBeanProperty());
if (features.serializableMode == SerializableMode.ACROSS_CLASSLOADERS) {
builder =
builder.implement(CrossClassLoaderSerializableMock.class)
.intercept(writeReplace);
}
if (readReplace != null) {
builder =
builder.defineMethod("readObject", void.class, Visibility.PRIVATE)
.withParameters(ObjectInputStream.class)
.throwing(ClassNotFoundException.class, IOException.class)
.intercept(readReplace);
}
if (name.startsWith(CODEGEN_PACKAGE) || classLoader instanceof MultipleParentClassLoader) {
builder =
builder.ignoreAlso(
isPackagePrivate()
.or(returns(isPackagePrivate()))
.or(hasParameters(whereAny(hasType(isPackagePrivate())))));
}
return builder.make()
.load(
classLoader,
loader.resolveStrategy(features.mockedType, classLoader, localMock))
.getLoaded();
}
mock方法生成的代理类
那么Mockito利用ByteBuddy生成的代理类是长怎么样子的?下面是一个被代理的List对象:
可以看到几点关键的地方:
- 持有一个MockMethodInterceptor对象
- 继承了MockAccess
- List原来的方法都被DispatcherDefaultingToRealMethod拦截了
MockMethodInterceptor.doIntercept方法
其中,DispatcherDefaultingToRealMethod
是MockMethodInterceptor
的内部类,最终它还是调用MockMethodInterceptor的doIntercept,而该方法中调用的是MockHanderImpl.handle方法:
Object doIntercept(
Object mock,
Method invokedMethod,
Object[] arguments,
RealMethod realMethod,
Location location)
throws Throwable {
// If the currently dispatched method is used in a hot path, typically a tight loop and if
// the mock is not used after the currently dispatched method, the JVM might attempt a
// garbage collection of the mock instance even before the execution of the current
// method is completed. Since we only reference the mock weakly from hereon after to avoid
// leaking the instance, it might therefore be garbage collected before the
// handler.handle(...) method completes. Since the handler method expects the mock to be
// present while a method call onto the mock is dispatched, this can lead to the problem
// described in GitHub #1802.
//
// To avoid this problem, we distract the JVM JIT by escaping the mock instance to a thread
// local field for the duration of the handler's dispatch.
//
// When dropping support for Java 8, instead of this hatch we should use an explicit fence
// https://docs.oracle.com/javase/9/docs/api/java/lang/ref/Reference.html#reachabilityFence-java.lang.Object-
weakReferenceHatch.set(mock);
try {
return handler.handle(
createInvocation(
mock,
invokedMethod,
arguments,
realMethod,
mockCreationSettings,
location));
} finally {
weakReferenceHatch.remove();
}
}
when&thenReturn
MOCKITO_CORE.when
when和thenReturn方法用于stub的创建,when方法调用的也是MockitoCore中方法:
@CheckReturnValue
public static <T> OngoingStubbing<T> when(T methodCall) {
return MOCKITO_CORE.when(methodCall);
}
实际调用的方法如下:
// 注意:when方法的参数methodCall实际上是无用的
public <T> OngoingStubbing<T> when(T methodCall) {
// 1.初始化MockProgressImpl对象
MockingProgress mockingProgress = mockingProgress();
// 2.标记stub开始
mockingProgress.stubbingStarted();
// 3.获取最新的ongoingStubbing对象
@SuppressWarnings("unchecked")
OngoingStubbing<T> stubbing = (OngoingStubbing<T>) mockingProgress.pullOngoingStubbing();
if (stubbing == null) {
mockingProgress.reset();
throw missingMethodInvocation();
}
// 4.返回ongoingStubbing对象
return stubbing;
}
其中OngoingStubbing对象会在每次MockHandlerImpl调用handle方法时创建一个,然后set到ThreadLocal的mockingProgress中,所以这里取出来的就是上一次的调用,这里也证明了其实when的参数是没用的,只要mock对象有方法调用就可以了。因此,when方法就是返回上次mock方法调用封装好的OngoingStubbing。
MockHandlerImpl.handle方法
上面说了所有mock对象的方法调用都会被MockMethodInterceptor
拦截,而MockMethodInterceptor最终会调到MockHandlerImpl
的handle方法。
/**
Invocation即InterceptedInvocation,把代理类拦截时的方法调用即参数封装在一起
它包含一下几个对象:真正的方法realMethod,Mockito的方法MockitoMethod,参数arguments,以及mock对象mockRef
private final MockReference<Object> mockRef;
private final MockitoMethod mockitoMethod;
private final Object[] arguments, rawArguments;
private final RealMethod realMethod;
*/
public Object handle(Invocation invocation) throws Throwable {
// 判断stub类型,对于doThrow和doAnswer先行处理
if (invocationContainer.hasAnswersForStubbing()) {
// stubbing voids with doThrow() or doAnswer() style
InvocationMatcher invocationMatcher =
matchersBinder.bindMatchers(
mockingProgress().getArgumentMatcherStorage(), invocation);
invocationContainer.setMethodForStubbing(invocationMatcher);
return null;
}
// 获取验证模式
VerificationMode verificationMode = mockingProgress().pullVerificationMode();
// 获取调用匹配器对象
InvocationMatcher invocationMatcher =
matchersBinder.bindMatchers(
mockingProgress().getArgumentMatcherStorage(), invocation);
mockingProgress().validateState();
// 如果调用过verify验证方法,则这里不为null
// if verificationMode is not null then someone is doing 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);
}
}
// OngoingStubbingImpl对象的创建位置,每次调用都会创建新的对象存在mockingProgress上下文中
// prepare invocation for stubbing
invocationContainer.setInvocationForPotentialStubbing(invocationMatcher);
OngoingStubbingImpl<T> ongoingStubbing = new OngoingStubbingImpl<T>(invocationContainer);
mockingProgress().reportOngoingStubbing(ongoingStubbing);
// 为当前拦截的调用对象,查找存在的返回值对象answer
// look for existing answer for this invocation
StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation);
// TODO #793 - when completed, we should be able to get rid of the casting below
notifyStubbedAnswerLookup(
invocation,
stubbing,
invocationContainer.getStubbingsAscending(),
(CreationSettings) mockSettings);
if (stubbing != null) {
stubbing.captureArgumentsFrom(invocation);
try {
// 这里执行的answer方法,即是thenReturn的返回内容
return stubbing.answer(invocation);
} finally {
// Needed so that we correctly isolate stubbings in some scenarios
// see MockitoStubbedCallInAnswerTest or issue #1279
mockingProgress().reportOngoingStubbing(ongoingStubbing);
}
} else {
// 如果打桩对象为空,也就是mock对象的默认行为,由于mock对象的每一个操作都是“无效”的,因此都会返回一个相应类型的默认值(stub除外)
Object ret = mockSettings.getDefaultAnswer().answer(invocation);
DefaultAnswerValidator.validateReturnValueFor(invocation, ret);
// Mockito uses it to redo setting invocation for potential stubbing in case of partial
// mocks / spies.
// Without it, the real method inside 'when' might have delegated to other self method
// and overwrite the intended stubbed method with a different one.
// This means we would be stubbing a wrong method.
// Typically this would led to runtime exception that validates return type with stubbed
// method signature.
invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher);
return ret;
}
}
when调用的基本形式是when(mock.doSome()), 此时,当mock.doSome()时即会触发上面的语句,其中:
OngoingStubbingImpl
表示正在对一个方法打桩的包装。invocationContainerImpl
相当于一个mock对象的管家,记录着mock对象方法的调用。mockingProgress
则可以理解为一个和线程相关的记录器,用于存放每个线程正要准备做的一些事情,它的内部包含了几个report 和 pull 这样的函数,如上所看到,mockingProgress记录着ongoingStubbing对象。
thenReturn方法
thenReturn是BaseStubbing的方法,其实它也是一个OngoingStubbing:
public interface OngoingStubbing<T> extends IOngoingStubbing {
OngoingStubbing<T> thenReturn(T var1);
OngoingStubbing<T> thenReturn(T var1, T... var2);
OngoingStubbing<T> thenThrow(Throwable... var1);
OngoingStubbing<T> thenThrow(Class<? extends Throwable>... var1);
OngoingStubbing<T> thenCallRealMethod();
OngoingStubbing<T> thenAnswer(Answer<?> var1);
OngoingStubbing<T> then(Answer<?> var1);
<M> M getMock();
}
OngoingStubbing.thenReturn方法
再回过头来看MOCKITO_CORE
里的when方法就会清晰许多,它会取出刚刚存放在mockingProgress中的ongoingStubbing对象。OngoingStubbing<T> stubbing = mockingProgress.pullOngoingStubbing();
,而thenReturn
、thenThrow
则是OngoingStubbing
里的方法,这些方法最终都会调到如下方法:
// OngoingStubbing#thenReturn
@Override
public OngoingStubbing<T> thenReturn(T value) {
return thenAnswer(new Returns(value));
}
// OngoingStubbingImpl#thenAnswer
@Override
public OngoingStubbing<T> thenAnswer(Answer<?> answer) {
if(!invocationContainer.hasInvocationForPotentialStubbing()) {
throw incorrectUseOfApi();
}
//把这个answer加入到invocationContainer对象中保存
invocationContainer.addAnswer(answer);
return new ConsecutiveStubbing<T>(invocationContainer);
}
// InvocationContainerImpl#addAnswer
public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive) {
// 1.获取stub的调用
Invocation invocation = invocationForStubbing.getInvocation();
// 2.标记stub完成
mockingProgress().stubbingCompleted();
if (answer instanceof ValidableAnswer) {
((ValidableAnswer) answer).validateFor(invocation);
}
// 3.把stub的调用和answer加到StubbedInvocationMatcher的list
// 这里的意思就是把调用和返回绑定,如果下次调用匹配到了,就返回对应的answer
synchronized (stubbed) {
if (isConsecutive) {
stubbed.getFirst().addAnswer(answer);
} else {
stubbed.addFirst(new StubbedInvocationMatcher(invocationForStubbing, answer));
}
return stubbed.getFirst();
}
}
那么调用的时候怎么匹配到answer呢?实际上还是在MockHandlerImpl
的handle方法,这里只节选了相关代码:
if (stubbing != null) {
stubbing.captureArgumentsFrom(invocation);
try {
//就是返回stub的answer方法
return stubbing.answer(invocation);
} finally {
//Needed so that we correctly isolate stubbings in some scenarios
//see MockitoStubbedCallInAnswerTest or issue #1279
mockingProgress().reportOngoingStubbing(ongoingStubbing);
}
}
其中answer方法如下:
//StubbedInvocationMatcher
public Object answer(InvocationOnMock invocation) throws Throwable {
// see ThreadsShareGenerouslyStubbedMockTest
Answer a;
// 从之前放进来的answers获取最新的那个
synchronized(answers) {
a = answers.size() == 1 ? answers.peek() : answers.poll();
}
return a.answer(invocation);
}
spy
spy 方法在能力和 mock 类似,spy 在构造代理对象时的差别仅为增加了一个真实对象的成员变量,并把返回模板替换为 CallsRealMethods
。
MOCKITO_CORE.mock方法
@CheckReturnValue
public static <T> T spy(T object) {
return MOCKITO_CORE.mock(
(Class<T>) object.getClass(),
withSettings().spiedInstance(object).defaultAnswer(CALLS_REAL_METHODS));
}
因此spy后的对象,如果没有对其中的方法mock,在answer方法调用时调用的是真实的方法(抽象方法会返回默认值):
// org.mockito.internal.stubbing.answers.CallsRealMethods#answer
public Object answer(InvocationOnMock invocation) throws Throwable {
if (Modifier.isAbstract(invocation.getMethod().getModifiers())) {
return RETURNS_DEFAULTS.answer(invocation);
}
return invocation.callRealMethod();
}
其他方法「待补充」
mockStatic
在mockito 3.x版本中,可以和powermock一样,对静态方法进行mock:
/**
* Creates a thread-local mock controller for all static methods of the given class or interface.
* The returned object's {@link MockedStatic#close()} method must be called upon completing the
* test or the mock will remain active on the current thread.
* <p>
* <b>Note</b>: We recommend against mocking static methods of classes in the standard library or
* classes used by custom class loaders used to executed the block with the mocked class. A mock
* maker might forbid mocking static methods of know classes that are known to cause problems.
* Also, if a static method is a JVM-intrinsic, it cannot typically be mocked even if not
* explicitly forbidden.
* <p>
* See examples in javadoc for {@link Mockito} class
*
* @param classToMock class or interface of which static mocks should be mocked.
* @return mock controller
*/
@Incubating
@CheckReturnValue
public static <T> MockedStatic<T> mockStatic(Class<T> classToMock) {
return mockStatic(classToMock, withSettings());
}
相较于mock方法的实现,这里主要多出了control对象的使用:
public <T> MockedStatic<T> mockStatic(Class<T> classToMock, MockSettings settings) {
if (!MockSettingsImpl.class.isInstance(settings)) {
throw new IllegalArgumentException(
"Unexpected implementation of '"
+ settings.getClass().getCanonicalName()
+ "'\n"
+ "At the moment, you cannot provide your own implementations of that class.");
}
MockSettingsImpl impl = MockSettingsImpl.class.cast(settings);
MockCreationSettings<T> creationSettings = impl.buildStatic(classToMock);
// 相较于mock方法的实现,这里主要多出了control对象的使用
MockMaker.StaticMockControl<T> control = createStaticMock(classToMock, creationSettings);
control.enable();
mockingProgress().mockingStarted(classToMock, creationSettings);
return new MockedStaticImpl<>(control);
}