一.基本构成
二.初始化过程
通过初始化MockClassLoader并通过该ClassLoader加载配置的@PrepareForTest类,通过javassit修改对应类的字节码,在方法调前加上类似的 Object localObject = MockGateway.methodCall(this, "testFinal", new Object[0], Desc.getParams("()"), ""); if (localObject != MockGateway.PROCEED) return,以满足特殊的mock需求。
- private void modifyMethod(CtMethod method, CtClass returnTypeAsCtClass,
- String returnTypeAsString) throws CannotCompileException {
- final String returnValue = getCorrectReturnValueType(returnTypeAsCtClass);
- String classOrInstance = classOrInstance(method);
- String code = "Object value = "
- + MockGateway.class.getName()
- + ".methodCall("
- + classOrInstance + ", \""
- + method.getName()
- + "\", $args, $sig, \""
- + returnTypeAsString
- + "\");"
- + "if (value != " + MockGateway.class.getName() + ".PROCEED) " + "return "
- + returnValue + "; ";
- method.insertBefore("{ " + code + "}");
- }
线程堆栈信息如下:
三.mock过程
PowerMockito的spy和mock方法最后都是调用的DefaultMockCreator.mock
- @Override
- public <T> T createMock(Class<T> type, boolean isStatic, boolean isSpy, Object delegator,
- MockSettings mockSettings, Method... methods) {
- if (type == null) {
- throw new IllegalArgumentException("The class to mock cannot be null");
- }
- validateType(type, isStatic, isSpy);
- final String mockName = toInstanceName(type, mockSettings);
- MockRepository.addAfterMethodRunner(new MockitoStateCleanerRunnable());
- final Class<T> typeToMock;
- if (isFinalJavaSystemClass(type)) {
- typeToMock = (Class<T>) new ClassReplicaCreator().createClassReplica(type);
- } else {
- typeToMock = type;
- }
- final MockData<T> mockData = createMethodInvocationControl(mockName, typeToMock, methods, isSpy, delegator,
- mockSettings);
- T mock = mockData.getMock();
- if (isFinalJavaSystemClass(type) && !isStatic) {
- mock = Whitebox.newInstance(type);
- DefaultFieldValueGenerator.fillWithDefaultValues(mock);
- }
- if (isStatic) {
- MockRepository.putStaticMethodInvocationControl(type, mockData.getMethodInvocationControl());
- } else {
- MockRepository.putInstanceMethodInvocationControl(mock, mockData.getMethodInvocationControl());
- }
- if (isSpy) {
- new LenientCopyTool().copyToMock(delegator, mock);
- }
- return mock;
- }
- private <T> MockData<T> createMethodInvocationControl(final String mockName, Class<T> type,
- Method[] methods, boolean isSpy, Object delegator, MockSettings mockSettings) {
- final MockSettingsImpl settings;
- if (mockSettings == null) {
- // We change the context classloader to the current CL in order for the Mockito
- // framework to load it's plugins (such as MockMaker) correctly.
- final ClassLoader originalCL = Thread.currentThread().getContextClassLoader();
- Thread.currentThread().setContextClassLoader(DefaultMockCreator.class.getClassLoader());
- try {
- settings = (MockSettingsImpl) Mockito.withSettings();
- } finally {
- Thread.currentThread().setContextClassLoader(originalCL);
- }
- } else {
- settings = (MockSettingsImpl) mockSettings;
- }
- if (isSpy) {
- settings.defaultAnswer(Mockito.CALLS_REAL_METHODS);
- }
- settings.setMockName(new MockNameImpl(mockName));
- settings.setTypeToMock(type);
- InternalMockHandler mockHandler = new MockHandlerFactory().create(settings);
- MethodInterceptorFilter filter = new PowerMockMethodInterceptorFilter(mockHandler, settings);
- final T mock = new ClassImposterizer(new InstantiatorProvider().getInstantiator(settings)).imposterise(filter, type);
- ClassLoader classLoader = mock.getClass().getClassLoader();
- if (classLoader instanceof MockClassLoader) {
- MockClassLoader mcl = (MockClassLoader) classLoader;
- mcl.cache(mock.getClass());
- }
- final MockitoMethodInvocationControl invocationControl = new MockitoMethodInvocationControl(
- filter,
- isSpy && delegator == null ? new Object() : delegator,
- mock,
- methods);
- return new MockData<T>(invocationControl, mock);
- }
- public <T> T imposterise(final MethodInterceptor interceptor, Class<T> mockedType, Class<?>... ancillaryTypes) {
- Class<Factory> proxyClass = null;
- Object proxyInstance = null;
- try {
- setConstructorsAccessible(mockedType, true);
- proxyClass = createProxyClass(mockedType, ancillaryTypes);
- proxyInstance = createProxy(proxyClass, interceptor);
- return mockedType.cast(proxyInstance);
- } catch (ClassCastException cce) {
- throw new MockitoException(join(
- "ClassCastException occurred while creating the mockito proxy :",
- " class to mock : " + describeClass(mockedType),
- " created class : " + describeClass(proxyClass),
- " proxy instance class : " + describeClass(proxyInstance),
- " instance creation by : " + instantiator.getClass().getSimpleName(),
- "",
- "You might experience classloading issues, disabling the Objenesis cache *might* help (see MockitoConfiguration)"
- ), cce);
- } finally {
- setConstructorsAccessible(mockedType, false);
- }
- }
四.mock类非final和static方法的调用
线程堆栈信息如下:
五.mock类的final方法调用
线程堆栈信息如下:
当spy时MockSettingsImpl.defaultAnswer为CallsRealMethods,其方法调用时MockedRealMethod会先调用notMockNextCallIfRequired新增MockGateway.DONT_MOCK_NEXT_CALL为true的AdditionalState,使其MockGateway.methodCall返回MockGateway.PROCEED从而调用原方法。
- public Object invoke(Object target, Object[] arguments) throws Throwable {
- /*
- * Instruct the MockGateway to don't intercept the next call.
- * The reason is that when Mockito is spying on objects it
- * should call the "real method" (which is proxied by Mockito
- * anyways) so that we don't end up in here one more time which
- * causes infinite recursion. This should not be done if the
- * interceptionObject is a final system class because these are
- * never caught by the Mockito proxy.
- */
- notMockNextCallIfRequired();
- try {
- return method.invoke(target, arguments);
- } catch (InvocationTargetException e) {
- SafeExceptionRethrower.safeRethrow(e.getCause());
- }
- return null;
- }
- private void notMockNextCallIfRequired() {
- final Class<?> type = Whitebox.getType(interceptionObject);
- if (isNextCallShouldNotBeMocked(type)) {
- MockRepository.putAdditionalState(MockGateway.DONT_MOCK_NEXT_CALL, true);
- }
- }