diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java index af7700e92f..1060636db4 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfigurationNoSecurityTests.java @@ -22,7 +22,7 @@ import org.junit.runner.RunWith; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.boot.testutil.ClassPathExclusions; -import org.springframework.boot.testutil.FilteredClassPathRunner; +import org.springframework.boot.testutil.ModifiedClassPathRunner; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -33,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Stephane Nicoll */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("spring-security-*.jar") public class ManagementServerPropertiesAutoConfigurationNoSecurityTests { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityHealthMvcEndpointIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityHealthMvcEndpointIntegrationTests.java index 2ddb91701e..0feaf7f230 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityHealthMvcEndpointIntegrationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/mvc/NoSpringSecurityHealthMvcEndpointIntegrationTests.java @@ -31,7 +31,7 @@ import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.testutil.ClassPathExclusions; -import org.springframework.boot.testutil.FilteredClassPathRunner; +import org.springframework.boot.testutil.ModifiedClassPathRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.mock.web.MockServletContext; @@ -49,7 +49,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. * * @author Andy Wilkinson */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("spring-security-*.jar") public class NoSpringSecurityHealthMvcEndpointIntegrationTests { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/OnBeanConditionTypeDeductionFailureTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/OnBeanConditionTypeDeductionFailureTests.java index 272834d4a5..0236b94ddf 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/OnBeanConditionTypeDeductionFailureTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/condition/OnBeanConditionTypeDeductionFailureTests.java @@ -22,7 +22,7 @@ import org.junit.runner.RunWith; import org.springframework.boot.autoconfigure.condition.OnBeanCondition.BeanTypeDeductionException; import org.springframework.boot.testutil.ClassPathExclusions; -import org.springframework.boot.testutil.FilteredClassPathRunner; +import org.springframework.boot.testutil.ModifiedClassPathRunner; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,7 +38,7 @@ import static org.junit.Assert.fail; * * @author Andy Wilkinson */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("jackson-core-*.jar") public class OnBeanConditionTypeDeductionFailureTests { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java index dbaeb9a112..5a044d4143 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourceBeanCreationFailureAnalyzerTests.java @@ -23,7 +23,7 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.testutil.ClassPathExclusions; -import org.springframework.boot.testutil.FilteredClassPathRunner; +import org.springframework.boot.testutil.ModifiedClassPathRunner; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; @@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Andy Wilkinson */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions({ "h2-*.jar", "hsqldb-*.jar" }) public class DataSourceBeanCreationFailureAnalyzerTests { diff --git a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsEmbeddedDataSourceAutoConfigurationTests.java b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsEmbeddedDataSourceAutoConfigurationTests.java index 4442faa9ff..9fc1a9eddf 100644 --- a/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsEmbeddedDataSourceAutoConfigurationTests.java +++ b/spring-boot-devtools/src/test/java/org/springframework/boot/devtools/autoconfigure/DevToolsEmbeddedDataSourceAutoConfigurationTests.java @@ -26,7 +26,7 @@ import org.junit.runner.RunWith; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.testutil.ClassPathExclusions; -import org.springframework.boot.testutil.FilteredClassPathRunner; +import org.springframework.boot.testutil.ModifiedClassPathRunner; import org.springframework.context.ConfigurableApplicationContext; import static org.mockito.Mockito.times; @@ -37,7 +37,7 @@ import static org.mockito.Mockito.verify; * * @author Andy Wilkinson */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("tomcat-jdbc-*.jar") public class DevToolsEmbeddedDataSourceAutoConfigurationTests extends AbstractDevToolsDataSourceAutoConfigurationTests { diff --git a/spring-boot-docs/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderExampleTests.java b/spring-boot-docs/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderExampleTests.java index 0287b21da8..9927cf0912 100644 --- a/spring-boot-docs/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderExampleTests.java +++ b/spring-boot-docs/src/test/java/org/springframework/boot/builder/SpringApplicationBuilderExampleTests.java @@ -22,7 +22,7 @@ import org.junit.runner.RunWith; import org.springframework.boot.test.rule.OutputCapture; import org.springframework.boot.testutil.ClassPathExclusions; -import org.springframework.boot.testutil.FilteredClassPathRunner; +import org.springframework.boot.testutil.ModifiedClassPathRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Andy Wilkinson */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("spring-web-*.jar") public class SpringApplicationBuilderExampleTests { diff --git a/spring-boot-test/pom.xml b/spring-boot-test/pom.xml index a0afed221a..d9f03e8234 100644 --- a/spring-boot-test/pom.xml +++ b/spring-boot-test/pom.xml @@ -112,6 +112,12 @@ true + + org.springframework.boot + spring-boot + test-jar + test + org.jetbrains.kotlin kotlin-runtime diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java index 5cef1c2cc5..63eab8bce5 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockReset.java @@ -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) { diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoAopProxyTargetInterceptor.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoAopProxyTargetInterceptor.java index 19507b27b8..1824c648a4 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoAopProxyTargetInterceptor.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/MockitoAopProxyTargetInterceptor.java @@ -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 matchers = storage.pullLocalizedMatchers(); - this.progress.verificationStarted(mode); - for (LocalizedMatcher matcher : matchers) { - storage.reportMatcher(matcher); - } + SpringBootMockUtil.mockingProgress().verificationStarted(mode); + SpringBootMockUtil.reportMatchers(storage, matchers); } } diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpringBootMockUtil.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpringBootMockUtil.java new file mode 100644 index 0000000000..f47dc11dec --- /dev/null +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpringBootMockUtil.java @@ -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 matchers) { + adapter.reportMatchers(storage, matchers); + } + + private interface MockUtilAdapter { + + MockCreationSettings getMockSettings(Object mock); + + MockingProgress mockingProgress(); + + void reportMatchers(ArgumentMatcherStorage storage, + List 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 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 matchers) { + for (LocalizedMatcher matcher : matchers) { + ReflectionUtils.invokeMethod(this.reportMatcherMethod, storage, + ReflectionUtils.invokeMethod(this.getMatcherMethod, matcher)); + } + } + + } + +} diff --git a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java index 252dd8ce6b..172b05aed2 100644 --- a/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java +++ b/spring-boot-test/src/main/java/org/springframework/boot/test/mock/mockito/SpyDefinition.java @@ -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 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()); diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java index 4f4dcbda24..cb8eaa8fea 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockDefinitionTests.java @@ -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"); diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/Mockito21Tests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/Mockito21Tests.java new file mode 100644 index 0000000000..495a772080 --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/Mockito21Tests.java @@ -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); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/Mockito22Tests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/Mockito22Tests.java new file mode 100644 index 0000000000..57df08053a --- /dev/null +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/Mockito22Tests.java @@ -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); + } + +} diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java index 8bfc6d9b1f..c9a1a55ae3 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/MockitoPostProcessorTests.java @@ -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 diff --git a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java index b92af3d2b5..47b800bbd2 100644 --- a/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java +++ b/spring-boot-test/src/test/java/org/springframework/boot/test/mock/mockito/SpyDefinitionTests.java @@ -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()) diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 5eb19b4b47..16b412cab1 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -290,6 +290,64 @@ org.apache.httpcomponents httpasyncclient test + + + org.apache.maven + maven-aether-provider + + + org.eclipse.sisu.plexus + org.eclipse.sisu + + + + + org.apache.maven + maven-settings-builder + + + org.codehaus.plexus + plexus-component-api + + + * + * + + + + + org.eclipse.aether + aether-api + + + org.eclipse.aether + aether-connector-basic + + + org.eclipse.aether + aether-impl + + + org.eclipse.aether + aether-spi + + + org.eclipse.aether + aether-transport-file + + + org.eclipse.aether + aether-transport-http + + + jcl-over-slf4j + org.slf4j + + + + + org.eclipse.aether + aether-util org.firebirdsql.jdbc diff --git a/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java b/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java index 14ebd3061f..e70e949f96 100644 --- a/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/ValidationExceptionFailureAnalyzerTests.java @@ -23,7 +23,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.diagnostics.FailureAnalysis; import org.springframework.boot.testutil.ClassPathExclusions; -import org.springframework.boot.testutil.FilteredClassPathRunner; +import org.springframework.boot.testutil.ModifiedClassPathRunner; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -34,7 +34,7 @@ import static org.junit.Assert.fail; * * @author Andy Wilkinson */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("hibernate-validator-*.jar") public class ValidationExceptionFailureAnalyzerTests { diff --git a/spring-boot/src/test/java/org/springframework/boot/testutil/ClassPathExclusions.java b/spring-boot/src/test/java/org/springframework/boot/testutil/ClassPathExclusions.java index 3781a38ec7..b42c053a64 100644 --- a/spring-boot/src/test/java/org/springframework/boot/testutil/ClassPathExclusions.java +++ b/spring-boot/src/test/java/org/springframework/boot/testutil/ClassPathExclusions.java @@ -23,7 +23,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * Annotation used in combination with {@link FilteredClassPathRunner} to exclude entries + * Annotation used in combination with {@link ModifiedClassPathRunner} to exclude entries * from the classpath. * * @author Andy Wilkinson diff --git a/spring-boot/src/test/java/org/springframework/boot/testutil/ClassPathOverrides.java b/spring-boot/src/test/java/org/springframework/boot/testutil/ClassPathOverrides.java new file mode 100644 index 0000000000..93af3c074e --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/testutil/ClassPathOverrides.java @@ -0,0 +1,42 @@ +/* + * 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.testutil; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation used in combination with {@link ModifiedClassPathRunner} to override entries + * on the classpath. + * + * @author Andy Wilkinson + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ClassPathOverrides { + + /** + * One or more sets of Maven coordinates ({@code groupId:artifactId:version}) to be + * added to the classpath. The additions will take precedence over any existing + * classes on the classpath. + * @return the coordinates + */ + String[] value(); + +} diff --git a/spring-boot/src/test/java/org/springframework/boot/testutil/FilteredClassPathRunner.java b/spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunner.java similarity index 64% rename from spring-boot/src/test/java/org/springframework/boot/testutil/FilteredClassPathRunner.java rename to spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunner.java index c89aa161e7..c7f05b4866 100644 --- a/spring-boot/src/test/java/org/springframework/boot/testutil/FilteredClassPathRunner.java +++ b/spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunner.java @@ -28,6 +28,22 @@ import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarFile; +import org.apache.maven.repository.internal.MavenRepositorySystemUtils; +import org.eclipse.aether.DefaultRepositorySystemSession; +import org.eclipse.aether.RepositorySystem; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.impl.DefaultServiceLocator; +import org.eclipse.aether.repository.LocalRepository; +import org.eclipse.aether.repository.RemoteRepository; +import org.eclipse.aether.resolution.ArtifactResult; +import org.eclipse.aether.resolution.DependencyRequest; +import org.eclipse.aether.resolution.DependencyResult; +import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; +import org.eclipse.aether.spi.connector.transport.TransporterFactory; +import org.eclipse.aether.transport.http.HttpTransporterFactory; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; @@ -38,16 +54,17 @@ import org.springframework.util.AntPathMatcher; import org.springframework.util.StringUtils; /** - * A custom {@link BlockJUnit4ClassRunner} that runs tests using a filtered class path. - * Entries are excluded from the class path using {@link ClassPathExclusions} on the test - * class. A class loader is created with the customized class path and is used both to - * load the test class and as the thread context class loader while the test is being run. + * A custom {@link BlockJUnit4ClassRunner} that runs tests using a modified class path. + * Entries are excluded from the class path using {@link ClassPathExclusions} and + * overridden using {@link ClassPathOverrides} on the test class. A class loader is + * created with the customized class path and is used both to load the test class and as + * the thread context class loader while the test is being run. * * @author Andy Wilkinson */ -public class FilteredClassPathRunner extends BlockJUnit4ClassRunner { +public class ModifiedClassPathRunner extends BlockJUnit4ClassRunner { - public FilteredClassPathRunner(Class testClass) throws InitializationError { + public ModifiedClassPathRunner(Class testClass) throws InitializationError { super(testClass); } @@ -64,7 +81,7 @@ public class FilteredClassPathRunner extends BlockJUnit4ClassRunner { private URLClassLoader createTestClassLoader(Class testClass) throws Exception { URLClassLoader classLoader = (URLClassLoader) this.getClass().getClassLoader(); - return new FilteredClassLoader(filterUrls(extractUrls(classLoader), testClass), + return new FilteredClassLoader(processUrls(extractUrls(classLoader), testClass), classLoader.getParent(), classLoader); } @@ -104,15 +121,61 @@ public class FilteredClassPathRunner extends BlockJUnit4ClassRunner { } } - private URL[] filterUrls(URL[] urls, Class testClass) throws Exception { + private URL[] processUrls(URL[] urls, Class testClass) throws Exception { ClassPathEntryFilter filter = new ClassPathEntryFilter(testClass); - List filteredUrls = new ArrayList(); + List processedUrls = new ArrayList(); + processedUrls.addAll(getAdditionalUrls(testClass)); for (URL url : urls) { if (!filter.isExcluded(url)) { - filteredUrls.add(url); + processedUrls.add(url); } } - return filteredUrls.toArray(new URL[filteredUrls.size()]); + return processedUrls.toArray(new URL[processedUrls.size()]); + } + + private List getAdditionalUrls(Class testClass) throws Exception { + ClassPathOverrides overrides = AnnotationUtils.findAnnotation(testClass, + ClassPathOverrides.class); + if (overrides == null) { + return Collections.emptyList(); + } + return resolveCoordinates(overrides.value()); + } + + private List resolveCoordinates(String[] coordinates) throws Exception { + DefaultServiceLocator serviceLocator = MavenRepositorySystemUtils + .newServiceLocator(); + serviceLocator.addService(RepositoryConnectorFactory.class, + BasicRepositoryConnectorFactory.class); + serviceLocator.addService(TransporterFactory.class, HttpTransporterFactory.class); + RepositorySystem repositorySystem = serviceLocator + .getService(RepositorySystem.class); + DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession(); + LocalRepository localRepository = new LocalRepository( + System.getProperty("user.home") + "/.m2/repository"); + session.setLocalRepositoryManager( + repositorySystem.newLocalRepositoryManager(session, localRepository)); + CollectRequest collectRequest = new CollectRequest(null, + Arrays.asList(new RemoteRepository.Builder("central", "default", + "http://central.maven.org/maven2").build())); + + collectRequest.setDependencies(createDependencies(coordinates)); + DependencyRequest dependencyRequest = new DependencyRequest(collectRequest, null); + DependencyResult result = repositorySystem.resolveDependencies(session, + dependencyRequest); + List resolvedArtifacts = new ArrayList(); + for (ArtifactResult artifact : result.getArtifactResults()) { + resolvedArtifacts.add(artifact.getArtifact().getFile().toURI().toURL()); + } + return resolvedArtifacts; + } + + private List createDependencies(String[] allCoordinates) { + List dependencies = new ArrayList(); + for (String coordinates : allCoordinates) { + dependencies.add(new Dependency(new DefaultArtifact(coordinates), null)); + } + return dependencies; } /** diff --git a/spring-boot/src/test/java/org/springframework/boot/testutil/FilteredClassPathRunnerTests.java b/spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunnerExclusionsTests.java similarity index 88% rename from spring-boot/src/test/java/org/springframework/boot/testutil/FilteredClassPathRunnerTests.java rename to spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunnerExclusionsTests.java index 73ef93cb80..40803d8e4b 100644 --- a/spring-boot/src/test/java/org/springframework/boot/testutil/FilteredClassPathRunnerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunnerExclusionsTests.java @@ -22,13 +22,13 @@ import org.junit.runner.RunWith; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link FilteredClassPathRunner} + * Tests for {@link ModifiedClassPathRunner} excluding entries from the class path. * * @author Andy Wilkinson */ -@RunWith(FilteredClassPathRunner.class) +@RunWith(ModifiedClassPathRunner.class) @ClassPathExclusions("hibernate-validator-*.jar") -public class FilteredClassPathRunnerTests { +public class ModifiedClassPathRunnerExclusionsTests { private static final String EXCLUDED_RESOURCE = "META-INF/services/" + "javax.validation.spi.ValidationProvider"; diff --git a/spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunnerOverridesTests.java b/spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunnerOverridesTests.java new file mode 100644 index 0000000000..8d51b23034 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/testutil/ModifiedClassPathRunnerOverridesTests.java @@ -0,0 +1,48 @@ +/* + * 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.testutil; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.context.ApplicationContext; +import org.springframework.util.StringUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ModifiedClassPathRunner} overriding entries on the class path. + * + * @author Andy Wilkinson + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathOverrides("org.springframework:spring-context:4.1.0.RELEASE") +public class ModifiedClassPathRunnerOverridesTests { + + @Test + public void classesAreLoadedFromOverride() { + assertThat(ApplicationContext.class.getProtectionDomain().getCodeSource() + .getLocation().toString()).endsWith("spring-context-4.1.0.RELEASE.jar"); + } + + @Test + public void classesAreLoadedFromTransitiveDependencyOfOverride() { + assertThat(StringUtils.class.getProtectionDomain().getCodeSource().getLocation() + .toString()).endsWith("spring-core-4.1.0.RELEASE.jar"); + } + +}