Make spring-boot-test compatible with Mockito 2.1 and 2.2
We use some internal Mockito classes and some breaking API changes have been made to them in Mockito 2. This commit introduces a utility class, SpringBootMockUtil, to shield our code from these differences. Mockito 1 is called directly and Mockito 2 is called via reflection. To allow these changes to be tested, FilteredClassPathRunner has been enhanced to also support overriding a dependency on the class path. As a result it has been renamed to ModifiedClassPathRunner. The new ClassPathOverrides annotation can be used to provide the Maven coordinates of one or more dependencies that should be resolved and added to the class path. Such additions are added to the start of the class path so that they override any existing dependency that contains the same classes. Closes gh-6520
This commit is contained in:
@@ -20,7 +20,6 @@ import java.util.List;
|
||||
|
||||
import org.mockito.MockSettings;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.internal.util.MockUtil;
|
||||
import org.mockito.listeners.InvocationListener;
|
||||
import org.mockito.listeners.MethodInvocationReport;
|
||||
import org.mockito.mock.MockCreationSettings;
|
||||
@@ -105,9 +104,8 @@ public enum MockReset {
|
||||
static MockReset get(Object mock) {
|
||||
MockReset reset = MockReset.NONE;
|
||||
if (ClassUtils.isPresent("org.mockito.internal.util.MockUtil", null)) {
|
||||
MockUtil mockUtil = new MockUtil();
|
||||
if (mockUtil.isMock(mock)) {
|
||||
MockCreationSettings settings = mockUtil.getMockSettings(mock);
|
||||
if (Mockito.mockingDetails(mock).isMock()) {
|
||||
MockCreationSettings settings = SpringBootMockUtil.getMockSettings(mock);
|
||||
List listeners = settings.getInvocationListeners();
|
||||
for (Object listener : listeners) {
|
||||
if (listener instanceof ResetInvocationListener) {
|
||||
|
||||
@@ -16,19 +16,14 @@
|
||||
|
||||
package org.springframework.boot.test.mock.mockito;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.Interceptor;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
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.stubbing.InvocationContainer;
|
||||
import org.mockito.internal.util.MockUtil;
|
||||
import org.mockito.internal.verification.MockAwareVerificationMode;
|
||||
import org.mockito.verification.VerificationMode;
|
||||
|
||||
@@ -38,7 +33,6 @@ import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.util.AopTestUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* AOP {@link Interceptor} that attempts to make AOP proxy beans work with Mockito. Works
|
||||
@@ -58,7 +52,7 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
|
||||
MockitoAopProxyTargetInterceptor(Object source, Object target) throws Exception {
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
this.verification = new Verification(target);
|
||||
this.verification = new Verification();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,21 +88,10 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
|
||||
|
||||
private final Object monitor = new Object();
|
||||
|
||||
private final MockingProgress progress;
|
||||
|
||||
Verification(Object target) {
|
||||
MockUtil mockUtil = new MockUtil();
|
||||
InternalMockHandler<?> handler = mockUtil.getMockHandler(target);
|
||||
InvocationContainer container = handler.getInvocationContainer();
|
||||
Field field = ReflectionUtils.findField(container.getClass(),
|
||||
"mockingProgress");
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
this.progress = (MockingProgress) ReflectionUtils.getField(field, container);
|
||||
}
|
||||
|
||||
public boolean isVerifying() {
|
||||
synchronized (this.monitor) {
|
||||
VerificationMode mode = this.progress.pullVerificationMode();
|
||||
VerificationMode mode = SpringBootMockUtil.mockingProgress()
|
||||
.pullVerificationMode();
|
||||
if (mode != null) {
|
||||
resetVerificationStarted(mode);
|
||||
return true;
|
||||
@@ -119,7 +102,8 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
|
||||
|
||||
public void replaceVerifyMock(Object source, Object target) {
|
||||
synchronized (this.monitor) {
|
||||
VerificationMode mode = this.progress.pullVerificationMode();
|
||||
VerificationMode mode = SpringBootMockUtil.mockingProgress()
|
||||
.pullVerificationMode();
|
||||
if (mode != null) {
|
||||
if (mode instanceof MockAwareVerificationMode) {
|
||||
MockAwareVerificationMode mockAwareMode = (MockAwareVerificationMode) mode;
|
||||
@@ -133,12 +117,11 @@ class MockitoAopProxyTargetInterceptor implements MethodInterceptor {
|
||||
}
|
||||
|
||||
private void resetVerificationStarted(VerificationMode mode) {
|
||||
ArgumentMatcherStorage storage = this.progress.getArgumentMatcherStorage();
|
||||
ArgumentMatcherStorage storage = SpringBootMockUtil.mockingProgress()
|
||||
.getArgumentMatcherStorage();
|
||||
List<LocalizedMatcher> matchers = storage.pullLocalizedMatchers();
|
||||
this.progress.verificationStarted(mode);
|
||||
for (LocalizedMatcher matcher : matchers) {
|
||||
storage.reportMatcher(matcher);
|
||||
}
|
||||
SpringBootMockUtil.mockingProgress().verificationStarted(mode);
|
||||
SpringBootMockUtil.reportMatchers(storage, matchers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.springframework.boot.test.mock.mockito;
|
||||
|
||||
import org.mockito.MockSettings;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.internal.util.MockUtil;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
@@ -33,8 +32,6 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
class SpyDefinition extends Definition {
|
||||
|
||||
private MockUtil mockUtil = new MockUtil();
|
||||
|
||||
private static final int MULTIPLIER = 31;
|
||||
|
||||
private final ResolvableType typeToSpy;
|
||||
@@ -87,7 +84,7 @@ class SpyDefinition extends Definition {
|
||||
public <T> T createSpy(String name, Object instance) {
|
||||
Assert.notNull(instance, "Instance must not be null");
|
||||
Assert.isInstanceOf(this.typeToSpy.resolve(), instance);
|
||||
if (this.mockUtil.isSpy(instance)) {
|
||||
if (Mockito.mockingDetails(instance).isSpy()) {
|
||||
return (T) instance;
|
||||
}
|
||||
MockSettings settings = MockReset.withSettings(getReset());
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.internal.util.MockUtil;
|
||||
import org.mockito.mock.MockCreationSettings;
|
||||
|
||||
import org.springframework.boot.test.mock.mockito.example.ExampleExtraInterface;
|
||||
@@ -86,7 +85,7 @@ public class MockDefinitionTests {
|
||||
new Class<?>[] { ExampleExtraInterface.class },
|
||||
Answers.RETURNS_SMART_NULLS, true, MockReset.BEFORE, null);
|
||||
ExampleService mock = definition.createMock();
|
||||
MockCreationSettings<?> settings = new MockUtil().getMockSettings(mock);
|
||||
MockCreationSettings<?> settings = SpringBootMockUtil.getMockSettings(mock);
|
||||
assertThat(mock).isInstanceOf(ExampleService.class);
|
||||
assertThat(mock).isInstanceOf(ExampleExtraInterface.class);
|
||||
assertThat(settings.getMockName().toString()).isEqualTo("name");
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.springframework.boot.testutil.ClassPathOverrides;
|
||||
import org.springframework.boot.testutil.ModifiedClassPathRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for compatibility with Mockito 2.1
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(ModifiedClassPathRunner.class)
|
||||
@ClassPathOverrides("org.mockito:mockito-core:2.1.0")
|
||||
public class Mockito21Tests {
|
||||
|
||||
@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);
|
||||
assertThat(result.getFailureCount()).isEqualTo(0);
|
||||
assertThat(result.getRunCount()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.springframework.boot.testutil.ClassPathOverrides;
|
||||
import org.springframework.boot.testutil.ModifiedClassPathRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for compatibility with Mockito 2.2
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@RunWith(ModifiedClassPathRunner.class)
|
||||
@ClassPathOverrides("org.mockito:mockito-core:2.2.0")
|
||||
public class Mockito22Tests {
|
||||
|
||||
@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);
|
||||
assertThat(result.getFailureCount()).isEqualTo(0);
|
||||
assertThat(result.getRunCount()).isGreaterThan(0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,7 @@ package org.springframework.boot.test.mock.mockito;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.internal.util.MockUtil;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@@ -80,7 +80,8 @@ public class MockitoPostProcessorTests {
|
||||
context.registerBeanDefinition("beanToBeMocked", factoryBeanDefinition);
|
||||
context.register(MockedFactoryBean.class);
|
||||
context.refresh();
|
||||
assertThat(new MockUtil().isMock(context.getBean("beanToBeMocked"))).isTrue();
|
||||
assertThat(Mockito.mockingDetails(context.getBean("beanToBeMocked")).isMock())
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -20,7 +20,6 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.internal.util.MockUtil;
|
||||
import org.mockito.mock.MockCreationSettings;
|
||||
|
||||
import org.springframework.boot.test.mock.mockito.example.ExampleService;
|
||||
@@ -79,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 = new MockUtil().getMockSettings(spy);
|
||||
MockCreationSettings<?> settings = SpringBootMockUtil.getMockSettings(spy);
|
||||
assertThat(spy).isInstanceOf(ExampleService.class);
|
||||
assertThat(settings.getMockName().toString()).isEqualTo("name");
|
||||
assertThat(settings.getDefaultAnswer())
|
||||
|
||||
Reference in New Issue
Block a user