/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.test.mock.mockito;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.hamcrest.Matcher;
import org.mockito.Answers;
import org.mockito.internal.InternalMockHandler;
import org.mockito.internal.matchers.LocalizedMatcher;
import org.mockito.internal.progress.ArgumentMatcherStorage;
import org.mockito.internal.progress.MockingProgress;
import org.mockito.internal.progress.ThreadSafeMockingProgress;
import org.mockito.internal.stubbing.InvocationContainer;
import org.mockito.internal.util.MockUtil;
import org.mockito.internal.verification.MockAwareVerificationMode;
import org.mockito.mock.MockCreationSettings;
import org.mockito.stubbing.Answer;
import org.mockito.verification.VerificationMode;
import org.springframework.beans.BeanUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* A facade for Mockito APIs that have changed between Mockito 1 and Mockito 2.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Phillip Webb
*/
abstract class MockitoApi {
private static final MockitoApi api = createApi();
/**
* Return mock settings for the given mock object.
* @param mock the mock object
* @return the mock creation settings
*/
public abstract MockCreationSettings<?> getMockSettings(Object mock);
/**
* Return the mocking progress for the current thread.
* @param mock the mock object
* @return the current mocking progress
*/
public abstract MockingProgress mockingProgress(Object mock);
/**
* Set report matchers to the given storage.
* @param storage the storage to use
* @param matchers the matchers to set
*/
public abstract void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers);
/**
* Create a new {@link MockAwareVerificationMode} instance.
* @param mock the source mock
* @param mode the verification mode
* @return a new {@link MockAwareVerificationMode} instance
*/
public abstract MockAwareVerificationMode createMockAwareVerificationMode(Object mock,
VerificationMode mode);
/**
* Return the {@link Answer} for a given {@link Answers} value.
* @param answer the source answers
* @return the answer
*/
public abstract Answer<Object> getAnswer(Answers answer);
/**
* Factory to create the appropriate API version.
* @return the API version
*/
private static MockitoApi createApi() {
if (ClassUtils.isPresent("org.mockito.ReturnValues", null)) {
return new Mockito1Api();
}
return new Mockito2Api();
}
/**
* Get the API for the running mockito version.
* @return the API
*/
public static MockitoApi get() {
return api;
}
/**
* {@link MockitoApi} for Mockito 1.0.
*/
private static class Mockito1Api extends MockitoApi {
private final MockUtil mockUtil;
private final Method getMockSettingsMethod;
private final Method getMockHandlerMethod;
private Method reportMatcherMethod;
private Constructor<MockAwareVerificationMode> mockAwareVerificationModeConstructor;
Mockito1Api() {
this.mockUtil = BeanUtils.instantiateClass(MockUtil.class);
this.getMockSettingsMethod = ReflectionUtils.findMethod(MockUtil.class,
"getMockSettings", Object.class);
this.getMockHandlerMethod = ReflectionUtils.findMethod(MockUtil.class,
"getMockHandler", Object.class);
this.reportMatcherMethod = ReflectionUtils.findMethod(
ArgumentMatcherStorage.class, "reportMatcher", Matcher.class);
this.mockAwareVerificationModeConstructor = ClassUtils
.getConstructorIfAvailable(MockAwareVerificationMode.class,
Object.class, VerificationMode.class);
}
@Override
public MockCreationSettings<?> getMockSettings(Object mock) {
return (MockCreationSettings<?>) ReflectionUtils
.invokeMethod(this.getMockSettingsMethod, this.mockUtil, mock);
}
@Override
public MockingProgress mockingProgress(Object mock) {
InternalMockHandler<?> handler = (InternalMockHandler<?>) ReflectionUtils
.invokeMethod(this.getMockHandlerMethod, this.mockUtil, mock);
InvocationContainer container = handler.getInvocationContainer();
Field field = ReflectionUtils.findField(container.getClass(),
"mockingProgress");
ReflectionUtils.makeAccessible(field);
return (MockingProgress) ReflectionUtils.getField(field, container);
}
@Override
public void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers) {
for (LocalizedMatcher matcher : matchers) {
ReflectionUtils.invokeMethod(this.reportMatcherMethod, storage, matcher);
}
}
@Override
public MockAwareVerificationMode createMockAwareVerificationMode(Object mock,
VerificationMode mode) {
return BeanUtils.instantiateClass(this.mockAwareVerificationModeConstructor,
mock, mode);
}
@Override
@SuppressWarnings("deprecation")
public Answer<Object> getAnswer(Answers answer) {
return answer.get();
}
}
/**
* {@link MockitoApi} for Mockito 2.0.
*/
private static class Mockito2Api extends MockitoApi {
@Override
public MockCreationSettings<?> getMockSettings(Object mock) {
return MockUtil.getMockSettings(mock);
}
@Override
public MockingProgress mockingProgress(Object mock) {
return ThreadSafeMockingProgress.mockingProgress();
}
@Override
public void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers) {
for (LocalizedMatcher matcher : matchers) {
storage.reportMatcher(matcher.getMatcher());
}
}
@Override
public MockAwareVerificationMode createMockAwareVerificationMode(Object mock,
VerificationMode mode) {
try {
return new MockAwareVerificationMode(mock, mode, Collections.emptySet());
}
catch (NoSuchMethodError ex) {
// Earlier versions of 2.x did not have the collection parameter
Constructor<MockAwareVerificationMode> constructor = ClassUtils
.getConstructorIfAvailable(MockAwareVerificationMode.class,
Object.class, VerificationMode.class);
if (constructor == null) {
throw ex;
}
return BeanUtils.instantiateClass(constructor, mock, mode);
}
}
@Override
public Answer<Object> getAnswer(Answers answer) {
return answer;
}
}
}