Mockito核心源码分析总结

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:
     *  &#064;{@link org.mockito.Mock}, &#064;{@link Spy}, &#064;{@link Captor}, &#064;{@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 &#64;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方法

  1. @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方法

  1. 其中创建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方法

  1. 其中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
     * &#064;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 &#064;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  的实现类 MockSettingsImplCreationSettings  承载着相关配置信息,比如要为待 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方法

其中,DispatcherDefaultingToRealMethodMockMethodInterceptor的内部类,最终它还是调用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();,而thenReturnthenThrow则是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);
    }

参考文章

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值