Polish Mockito 2.0 support

This commit is contained in:
Phillip Webb 2017-01-05 16:28:59 -08:00
parent 565f75438e
commit abb8e3663a
7 changed files with 301 additions and 155 deletions

View File

@ -105,7 +105,7 @@ public enum MockReset {
MockReset reset = MockReset.NONE;
if (ClassUtils.isPresent("org.mockito.internal.util.MockUtil", null)) {
if (Mockito.mockingDetails(mock).isMock()) {
MockCreationSettings settings = SpringBootMockUtil.getMockSettings(mock);
MockCreationSettings settings = MockitoApi.get().getMockSettings(mock);
List listeners = settings.getInvocationListeners();
for (Object listener : listeners) {
if (listener instanceof ResetInvocationListener) {

View File

@ -24,6 +24,7 @@ import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.mockito.internal.matchers.LocalizedMatcher;
import org.mockito.internal.progress.ArgumentMatcherStorage;
import org.mockito.internal.progress.MockingProgress;
import org.mockito.internal.verification.MockAwareVerificationMode;
import org.mockito.verification.VerificationMode;
@ -52,7 +53,7 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
MockitoAopProxyTargetInterceptor(Object source, Object target) throws Exception {
this.source = source;
this.target = target;
this.verification = new Verification();
this.verification = new Verification(target);
}
@Override
@ -88,10 +89,15 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
private final Object monitor = new Object();
private final MockingProgress progress;
public Verification(Object target) {
this.progress = MockitoApi.get().mockingProgress(target);
}
public boolean isVerifying() {
synchronized (this.monitor) {
VerificationMode mode = SpringBootMockUtil.mockingProgress()
.pullVerificationMode();
VerificationMode mode = this.progress.pullVerificationMode();
if (mode != null) {
resetVerificationStarted(mode);
return true;
@ -102,13 +108,13 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
public void replaceVerifyMock(Object source, Object target) {
synchronized (this.monitor) {
VerificationMode mode = SpringBootMockUtil.mockingProgress()
.pullVerificationMode();
VerificationMode mode = this.progress.pullVerificationMode();
if (mode != null) {
if (mode instanceof MockAwareVerificationMode) {
MockAwareVerificationMode mockAwareMode = (MockAwareVerificationMode) mode;
if (mockAwareMode.getMock() == source) {
mode = new MockAwareVerificationMode(target, mockAwareMode);
mode = MockitoApi.get().createMockAwareVerificationMode(
target, mockAwareMode);
}
}
resetVerificationStarted(mode);
@ -117,11 +123,10 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
}
private void resetVerificationStarted(VerificationMode mode) {
ArgumentMatcherStorage storage = SpringBootMockUtil.mockingProgress()
.getArgumentMatcherStorage();
ArgumentMatcherStorage storage = this.progress.getArgumentMatcherStorage();
List<LocalizedMatcher> matchers = storage.pullLocalizedMatchers();
SpringBootMockUtil.mockingProgress().verificationStarted(mode);
SpringBootMockUtil.reportMatchers(storage, matchers);
this.progress.verificationStarted(mode);
MockitoApi.get().reportMatchers(storage, matchers);
}
}

View File

@ -0,0 +1,224 @@
/*
* Copyright 2012-2016 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 java.util.Set;
import org.mockito.Answers;
import org.mockito.ArgumentMatcher;
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 bean under test
* @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.quality.MockitoHint", null)) {
return new Mockito2Api();
}
return new Mockito1Api();
}
/**
* 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 {
@Override
public MockCreationSettings<?> getMockSettings(Object mock) {
return new MockUtil().getMockSettings(mock);
}
@Override
public MockingProgress mockingProgress(Object mock) {
MockUtil mockUtil = new MockUtil();
InternalMockHandler<?> handler = mockUtil.getMockHandler(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) {
storage.reportMatcher(matcher);
}
}
@Override
public MockAwareVerificationMode createMockAwareVerificationMode(Object mock,
VerificationMode mode) {
return new MockAwareVerificationMode(mock, mode);
}
@Override
public Answer<Object> getAnswer(Answers answer) {
return answer.get();
}
}
/**
* {@link MockitoApi} for Mockito 2.0.
*/
private static class Mockito2Api extends MockitoApi {
private final Method getMockSettingsMethod;
private final Method mockingProgressMethod;
private final Method reportMatcherMethod;
private final Method getMatcherMethod;
private Constructor<MockAwareVerificationMode> mockAwareVerificationModeConstructor;
public Mockito2Api() {
this.getMockSettingsMethod = ReflectionUtils.findMethod(MockUtil.class,
"getMockSettings", Object.class);
this.mockingProgressMethod = ReflectionUtils
.findMethod(ThreadSafeMockingProgress.class, "mockingProgress");
this.reportMatcherMethod = ReflectionUtils.findMethod(
ArgumentMatcherStorage.class, "reportMatcher", ArgumentMatcher.class);
this.getMatcherMethod = ReflectionUtils.findMethod(LocalizedMatcher.class,
"getMatcher");
this.mockAwareVerificationModeConstructor = ClassUtils
.getConstructorIfAvailable(MockAwareVerificationMode.class,
Object.class, VerificationMode.class, Set.class);
}
@Override
public MockCreationSettings<?> getMockSettings(Object mock) {
return (MockCreationSettings<?>) ReflectionUtils
.invokeMethod(this.getMockSettingsMethod, null, mock);
}
@Override
public MockingProgress mockingProgress(Object mock) {
return (MockingProgress) ReflectionUtils
.invokeMethod(this.mockingProgressMethod, null);
}
@Override
public void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers) {
for (LocalizedMatcher matcher : matchers) {
ReflectionUtils.invokeMethod(this.reportMatcherMethod, storage,
ReflectionUtils.invokeMethod(this.getMatcherMethod, matcher));
}
}
@Override
public MockAwareVerificationMode createMockAwareVerificationMode(Object mock,
VerificationMode mode) {
if (this.mockAwareVerificationModeConstructor != null) {
// Later 2.0 releases include a listener set
return BeanUtils.instantiateClass(
this.mockAwareVerificationModeConstructor, mock, mode,
Collections.emptySet());
}
return new MockAwareVerificationMode(mock, mode);
}
@Override
@SuppressWarnings("unchecked")
public Answer<Object> getAnswer(Answers answer) {
return (Answer<Object>) ((Object) answer);
}
}
}

View File

@ -1,142 +0,0 @@
/*
* Copyright 2012-2016 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.Method;
import java.util.List;
import org.mockito.ArgumentMatcher;
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.util.MockUtil;
import org.mockito.mock.MockCreationSettings;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* A facade for Mockito's {@link MockUtil} that hides API differences between Mockito 1
* and 2.
*
* @author Andy Wilkinson
*/
final class SpringBootMockUtil {
private static final MockUtilAdapter adapter;
static {
if (ClassUtils.isPresent("org.mockito.quality.MockitoHint",
SpringBootMockUtil.class.getClassLoader())) {
adapter = new Mockito2MockUtilAdapter();
}
else {
adapter = new Mockito1MockUtilAdapter();
}
}
private SpringBootMockUtil() {
}
static MockCreationSettings<?> getMockSettings(Object mock) {
return adapter.getMockSettings(mock);
}
static MockingProgress mockingProgress() {
return adapter.mockingProgress();
}
static void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers) {
adapter.reportMatchers(storage, matchers);
}
private interface MockUtilAdapter {
MockCreationSettings<?> getMockSettings(Object mock);
MockingProgress mockingProgress();
void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers);
}
private static class Mockito1MockUtilAdapter implements MockUtilAdapter {
private static final MockingProgress mockingProgress = new ThreadSafeMockingProgress();
@Override
public MockCreationSettings<?> getMockSettings(Object mock) {
return new MockUtil().getMockSettings(mock);
}
@Override
public MockingProgress mockingProgress() {
return mockingProgress;
}
@Override
public void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers) {
for (LocalizedMatcher matcher : matchers) {
storage.reportMatcher(matcher);
}
}
}
private static class Mockito2MockUtilAdapter implements MockUtilAdapter {
private final Method getMockSettingsMethod = ReflectionUtils
.findMethod(MockUtil.class, "getMockSettings", Object.class);
private final Method mockingProgressMethod = ReflectionUtils
.findMethod(ThreadSafeMockingProgress.class, "mockingProgress");
private final Method reportMatcherMethod = ReflectionUtils.findMethod(
ArgumentMatcherStorage.class, "reportMatcher", ArgumentMatcher.class);
private final Method getMatcherMethod = ReflectionUtils
.findMethod(LocalizedMatcher.class, "getMatcher");
@Override
public MockCreationSettings<?> getMockSettings(Object mock) {
return (MockCreationSettings<?>) ReflectionUtils
.invokeMethod(this.getMockSettingsMethod, null, mock);
}
@Override
public MockingProgress mockingProgress() {
return (MockingProgress) ReflectionUtils
.invokeMethod(this.mockingProgressMethod, null);
}
@Override
public void reportMatchers(ArgumentMatcherStorage storage,
List<LocalizedMatcher> matchers) {
for (LocalizedMatcher matcher : matchers) {
ReflectionUtils.invokeMethod(this.reportMatcherMethod, storage,
ReflectionUtils.invokeMethod(this.getMatcherMethod, matcher));
}
}
}
}

View File

@ -85,7 +85,7 @@ public class MockDefinitionTests {
new Class<?>[] { ExampleExtraInterface.class },
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, null);
ExampleService mock = definition.createMock();
MockCreationSettings<?> settings = SpringBootMockUtil.getMockSettings(mock);
MockCreationSettings<?> settings = MockitoApi.get().getMockSettings(mock);
assertThat(mock).isInstanceOf(ExampleService.class);
assertThat(mock).isInstanceOf(ExampleExtraInterface.class);
assertThat(settings.getMockName().toString()).isEqualTo("name");

View File

@ -0,0 +1,59 @@
/*
* Copyright 2012-2016 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 org.junit.Test;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.RunWith;
import org.junit.runner.notification.Failure;
import org.springframework.boot.junit.runner.classpath.ClassPathOverrides;
import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for compatibility with Mockito 2.5
*
* @author Andy Wilkinson
* @author Phillip Webb
*/
@RunWith(ModifiedClassPathRunner.class)
@ClassPathOverrides("org.mockito:mockito-core:2.5.4")
public class Mockito25Tests {
@Test
public void resetMocksTestExecutionListenerTestsWithMockito2() {
runTests(ResetMocksTestExecutionListenerTests.class);
}
@Test
public void spyBeanWithAopProxyTestsWithMockito2() {
runTests(SpyBeanWithAopProxyTests.class);
}
private void runTests(Class<?> testClass) {
Result result = new JUnitCore().run(testClass);
for (Failure failure : result.getFailures()) {
System.err.println(failure.getTrace());
}
assertThat(result.getFailureCount()).isEqualTo(0);
assertThat(result.getRunCount()).isGreaterThan(0);
}
}

View File

@ -78,7 +78,7 @@ public class SpyDefinitionTests {
SpyDefinition definition = new SpyDefinition("name", REAL_SERVICE_TYPE,
MockReset.BEFORE, true, null);
RealExampleService spy = definition.createSpy(new RealExampleService("hello"));
MockCreationSettings<?> settings = SpringBootMockUtil.getMockSettings(spy);
MockCreationSettings<?> settings = MockitoApi.get().getMockSettings(spy);
assertThat(spy).isInstanceOf(ExampleService.class);
assertThat(settings.getMockName().toString()).isEqualTo("name");
assertThat(settings.getDefaultAnswer())