diff --git a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc index 628edcc21d..d4a41beb20 100644 --- a/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc +++ b/framework-docs/modules/ROOT/pages/testing/testcontext-framework/tel-config.adoc @@ -10,6 +10,7 @@ by default, exactly in the following order: annotation for "`before`" modes. * `ApplicationEventsTestExecutionListener`: Provides support for xref:testing/testcontext-framework/application-events.adoc[`ApplicationEvents`]. +* `BeanOverrideTestExecutionListener`: Provides support for xref:testing/testcontext-framework/bean-overriding.adoc[] . * `DependencyInjectionTestExecutionListener`: Provides dependency injection for the test instance. * `MicrometerObservationRegistryTestExecutionListener`: Provides support for @@ -25,7 +26,6 @@ by default, exactly in the following order: * `EventPublishingTestExecutionListener`: Publishes test execution events to the test's `ApplicationContext` (see xref:testing/testcontext-framework/test-execution-events.adoc[Test Execution Events]). * `MockitoResetTestExecutionListener`: Resets mocks as configured by `@MockitoBean` or `@MockitoSpyBean`. -* `BeanOverrideTestExecutionListener`: Provides support for xref:testing/testcontext-framework/bean-overriding.adoc[] . [[testcontext-tel-config-registering-tels]] == Registering `TestExecutionListener` Implementations diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java index a2d81e84b2..ad8b6ba180 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListener.java @@ -66,6 +66,8 @@ package org.springframework.test.context; * DirtiesContextBeforeModesTestExecutionListener} *
  • {@link org.springframework.test.context.event.ApplicationEventsTestExecutionListener * ApplicationEventsTestExecutionListener}
  • + *
  • {@link org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener + * BeanOverrideTestExecutionListener}
  • *
  • {@link org.springframework.test.context.support.DependencyInjectionTestExecutionListener * DependencyInjectionTestExecutionListener}
  • *
  • {@link org.springframework.test.context.observation.MicrometerObservationRegistryTestExecutionListener @@ -82,8 +84,6 @@ package org.springframework.test.context; * EventPublishingTestExecutionListener}
  • *
  • {@link org.springframework.test.context.bean.override.mockito.MockitoResetTestExecutionListener * MockitoResetTestExecutionListener}
  • - *
  • {@link org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener - * BeanOverrideTestExecutionListener}
  • * * * @author Sam Brannen diff --git a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java index 2bb68cceb5..a06ea72d1b 100644 --- a/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java +++ b/spring-test/src/main/java/org/springframework/test/context/TestExecutionListeners.java @@ -82,6 +82,7 @@ public @interface TestExecutionListeners { * @see org.springframework.test.context.web.ServletTestExecutionListener * @see org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener * @see org.springframework.test.context.event.ApplicationEventsTestExecutionListener + * @see org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener * @see org.springframework.test.context.support.DependencyInjectionTestExecutionListener * @see org.springframework.test.context.support.DirtiesContextTestExecutionListener * @see org.springframework.test.context.support.CommonCachesTestExecutionListener @@ -89,7 +90,6 @@ public @interface TestExecutionListeners { * @see org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener * @see org.springframework.test.context.event.EventPublishingTestExecutionListener * @see org.springframework.test.context.bean.override.mockito.MockitoResetTestExecutionListener - * @see org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener */ @AliasFor("value") Class[] listeners() default {}; diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java index 4b9e77369a..802d9cf977 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/BeanOverrideTestExecutionListener.java @@ -37,11 +37,15 @@ import org.springframework.test.context.support.DependencyInjectionTestExecution public class BeanOverrideTestExecutionListener extends AbstractTestExecutionListener { /** - * Executes almost last ({@code LOWEST_PRECEDENCE - 50}). + * Returns {@code 1950}, which ensures that the {@code BeanOverrideTestExecutionListener} + * is ordered after the + * {@link org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener + * DirtiesContextBeforeModesTestExecutionListener} and just before the + * {@link DependencyInjectionTestExecutionListener}. */ @Override public int getOrder() { - return LOWEST_PRECEDENCE - 50; + return 1950; } /** diff --git a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java index bd95093fa0..8ec8c02170 100644 --- a/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java +++ b/spring-test/src/main/java/org/springframework/test/context/bean/override/mockito/MockitoResetTestExecutionListener.java @@ -54,7 +54,7 @@ import org.springframework.util.ClassUtils; */ public class MockitoResetTestExecutionListener extends AbstractTestExecutionListener { - static final boolean mockitoPresent = ClassUtils.isPresent("org.mockito.Mockito", + private static final boolean mockitoPresent = ClassUtils.isPresent("org.mockito.Mockito", MockitoResetTestExecutionListener.class.getClassLoader()); private static final String SPRING_MOCKITO_PACKAGE = "org.springframework.test.context.bean.override.mockito"; @@ -62,8 +62,9 @@ public class MockitoResetTestExecutionListener extends AbstractTestExecutionList private static final Predicate> isSpringMockitoAnnotation = mergedAnnotation -> mergedAnnotation.getType().getPackageName().equals(SPRING_MOCKITO_PACKAGE); + /** - * Executes before {@link org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener}. + * Returns {@code Ordered.LOWEST_PRECEDENCE - 100}. */ @Override public int getOrder() { diff --git a/spring-test/src/main/resources/META-INF/spring.factories b/spring-test/src/main/resources/META-INF/spring.factories index 57c10c50bf..344b3ecb20 100644 --- a/spring-test/src/main/resources/META-INF/spring.factories +++ b/spring-test/src/main/resources/META-INF/spring.factories @@ -4,6 +4,7 @@ org.springframework.test.context.TestExecutionListener = \ org.springframework.test.context.web.ServletTestExecutionListener,\ org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener,\ org.springframework.test.context.event.ApplicationEventsTestExecutionListener,\ + org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener,\ org.springframework.test.context.support.DependencyInjectionTestExecutionListener,\ org.springframework.test.context.observation.MicrometerObservationRegistryTestExecutionListener,\ org.springframework.test.context.support.DirtiesContextTestExecutionListener,\ @@ -11,8 +12,7 @@ org.springframework.test.context.TestExecutionListener = \ org.springframework.test.context.transaction.TransactionalTestExecutionListener,\ org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener,\ org.springframework.test.context.event.EventPublishingTestExecutionListener,\ - org.springframework.test.context.bean.override.mockito.MockitoResetTestExecutionListener,\ - org.springframework.test.context.bean.override.BeanOverrideTestExecutionListener + org.springframework.test.context.bean.override.mockito.MockitoResetTestExecutionListener # Default ContextCustomizerFactory implementations for the Spring TestContext Framework # diff --git a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java index ced6f3cba0..001dfd8121 100644 --- a/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java +++ b/spring-test/src/test/java/org/springframework/test/context/TestExecutionListenersTests.java @@ -68,6 +68,7 @@ class TestExecutionListenersTests { List> expected = asList(ServletTestExecutionListener.class,// DirtiesContextBeforeModesTestExecutionListener.class,// ApplicationEventsTestExecutionListener.class,// + BeanOverrideTestExecutionListener.class,// DependencyInjectionTestExecutionListener.class,// micrometerListenerClass,// DirtiesContextTestExecutionListener.class,// @@ -75,8 +76,7 @@ class TestExecutionListenersTests { TransactionalTestExecutionListener.class,// SqlScriptsTestExecutionListener.class,// EventPublishingTestExecutionListener.class,// - MockitoResetTestExecutionListener.class,// - BeanOverrideTestExecutionListener.class + MockitoResetTestExecutionListener.class// ); assertRegisteredListeners(DefaultListenersTestCase.class, expected); } @@ -90,6 +90,7 @@ class TestExecutionListenersTests { ServletTestExecutionListener.class,// DirtiesContextBeforeModesTestExecutionListener.class,// ApplicationEventsTestExecutionListener.class,// + BeanOverrideTestExecutionListener.class,// DependencyInjectionTestExecutionListener.class,// micrometerListenerClass,// DirtiesContextTestExecutionListener.class,// @@ -97,8 +98,7 @@ class TestExecutionListenersTests { TransactionalTestExecutionListener.class,// SqlScriptsTestExecutionListener.class,// EventPublishingTestExecutionListener.class,// - MockitoResetTestExecutionListener.class,// - BeanOverrideTestExecutionListener.class + MockitoResetTestExecutionListener.class// ); assertRegisteredListeners(MergedDefaultListenersWithCustomListenerPrependedTestCase.class, expected); } @@ -111,6 +111,7 @@ class TestExecutionListenersTests { List> expected = asList(ServletTestExecutionListener.class,// DirtiesContextBeforeModesTestExecutionListener.class,// ApplicationEventsTestExecutionListener.class,// + BeanOverrideTestExecutionListener.class,// DependencyInjectionTestExecutionListener.class,// micrometerListenerClass,// DirtiesContextTestExecutionListener.class,// @@ -119,7 +120,6 @@ class TestExecutionListenersTests { SqlScriptsTestExecutionListener.class,// EventPublishingTestExecutionListener.class,// MockitoResetTestExecutionListener.class,// - BeanOverrideTestExecutionListener.class,// BazTestExecutionListener.class ); assertRegisteredListeners(MergedDefaultListenersWithCustomListenerAppendedTestCase.class, expected); @@ -133,6 +133,7 @@ class TestExecutionListenersTests { List> expected = asList(ServletTestExecutionListener.class,// DirtiesContextBeforeModesTestExecutionListener.class,// ApplicationEventsTestExecutionListener.class,// + BeanOverrideTestExecutionListener.class,// DependencyInjectionTestExecutionListener.class,// BarTestExecutionListener.class,// micrometerListenerClass,// @@ -141,8 +142,7 @@ class TestExecutionListenersTests { TransactionalTestExecutionListener.class,// SqlScriptsTestExecutionListener.class,// EventPublishingTestExecutionListener.class,// - MockitoResetTestExecutionListener.class,// - BeanOverrideTestExecutionListener.class + MockitoResetTestExecutionListener.class// ); assertRegisteredListeners(MergedDefaultListenersWithCustomListenerInsertedTestCase.class, expected); } diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanWithDirtiesContextBeforeMethodIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanWithDirtiesContextBeforeMethodIntegrationTests.java new file mode 100644 index 0000000000..200af51e5e --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/convention/TestBeanWithDirtiesContextBeforeMethodIntegrationTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2002-2024 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 + * + * https://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.test.context.bean.override.convention; + +import org.junit.jupiter.api.RepeatedTest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.MethodMode; +import org.springframework.test.context.bean.override.example.ExampleService; +import org.springframework.test.context.bean.override.example.ExampleServiceCaller; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.springframework.test.annotation.DirtiesContext.MethodMode.BEFORE_METHOD; + +/** + * Integration tests for using {@link TestBean @TestBean} with + * {@link DirtiesContext @DirtiesContext} and {@link MethodMode#BEFORE_METHOD}. + * + * @author Sam Brannen + * @since 6.2 + */ +@SpringJUnitConfig +class TestBeanWithDirtiesContextBeforeMethodIntegrationTests { + + @Autowired + ExampleServiceCaller caller; + + @TestBean + ExampleService service; + + @Autowired + ExampleService autowiredService; + + + static ExampleService service() { + return mock(); + } + + + @RepeatedTest(2) + @DirtiesContext(methodMode = BEFORE_METHOD) + void testOverride() { + assertThat(service).isSameAs(autowiredService); + + given(service.greeting()).willReturn("Spring"); + assertThat(caller.sayGreeting()).isEqualTo("I say Spring"); + } + + + @Configuration(proxyBeanMethods = false) + @Import(ExampleServiceCaller.class) + static class Config { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/example/SimpleExampleService.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/example/SimpleExampleService.java new file mode 100644 index 0000000000..da9d647148 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/example/SimpleExampleService.java @@ -0,0 +1,31 @@ +/* + * Copyright 2002-2024 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 + * + * https://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.test.context.bean.override.example; + +/** + * Example service implementation for spy tests. + * + * @author Phillip Webb + * @since 6.2 + */ +public class SimpleExampleService extends RealExampleService { + + public SimpleExampleService() { + super("simple"); + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoBeanWithDirtiesContextBeforeMethodIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoBeanWithDirtiesContextBeforeMethodIntegrationTests.java new file mode 100644 index 0000000000..2195645112 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoBeanWithDirtiesContextBeforeMethodIntegrationTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2002-2024 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 + * + * https://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.test.context.bean.override.mockito.integration; + +import org.junit.jupiter.api.RepeatedTest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.MethodMode; +import org.springframework.test.context.bean.override.example.ExampleService; +import org.springframework.test.context.bean.override.example.ExampleServiceCaller; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.springframework.test.annotation.DirtiesContext.MethodMode.BEFORE_METHOD; + +/** + * Integration tests for using {@link MockitoBean @MockitoBean} with + * {@link DirtiesContext @DirtiesContext} and {@link MethodMode#BEFORE_METHOD}. + * + * @author Andy Wilkinson + * @author Sam Brannen + * @since 6.2 + * @see MockitoSpyBeanWithDirtiesContextBeforeMethodIntegrationTests + */ +@SpringJUnitConfig +class MockitoBeanWithDirtiesContextBeforeMethodIntegrationTests { + + @Autowired + ExampleServiceCaller caller; + + @MockitoBean + ExampleService service; + + @Autowired + ExampleService autowiredService; + + + @RepeatedTest(2) + @DirtiesContext(methodMode = BEFORE_METHOD) + void testMocking() { + assertThat(service).isSameAs(autowiredService); + + given(service.greeting()).willReturn("Spring"); + assertThat(caller.sayGreeting()).isEqualTo("I say Spring"); + } + + + @Configuration(proxyBeanMethods = false) + @Import(ExampleServiceCaller.class) + static class Config { + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoSpyBeanWithDirtiesContextBeforeMethodIntegrationTests.java b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoSpyBeanWithDirtiesContextBeforeMethodIntegrationTests.java new file mode 100644 index 0000000000..7fdf068007 --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/bean/override/mockito/integration/MockitoSpyBeanWithDirtiesContextBeforeMethodIntegrationTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2002-2024 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 + * + * https://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.test.context.bean.override.mockito.integration; + +import org.junit.jupiter.api.RepeatedTest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.annotation.DirtiesContext.MethodMode; +import org.springframework.test.context.bean.override.example.ExampleService; +import org.springframework.test.context.bean.override.example.ExampleServiceCaller; +import org.springframework.test.context.bean.override.example.SimpleExampleService; +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.then; +import static org.springframework.test.annotation.DirtiesContext.MethodMode.BEFORE_METHOD; + +/** + * Integration tests for using {@link MockitoSpyBean @MockitoSpyBean} with + * {@link DirtiesContext @DirtiesContext} and {@link MethodMode#BEFORE_METHOD}. + * + * @author Andy Wilkinson + * @author Sam Brannen + * @since 6.2 + * @see MockitoBeanWithDirtiesContextBeforeMethodIntegrationTests + */ +@SpringJUnitConfig +class MockitoSpyBeanWithDirtiesContextBeforeMethodIntegrationTests { + + @Autowired + ExampleServiceCaller caller; + + @MockitoSpyBean + SimpleExampleService service; + + @Autowired + ExampleService autowiredService; + + + @RepeatedTest(2) + @DirtiesContext(methodMode = BEFORE_METHOD) + void testSpying() { + assertThat(service).isSameAs(autowiredService); + + assertThat(caller.sayGreeting()).isEqualTo("I say simple"); + then(service).should().greeting(); + } + + + @Configuration(proxyBeanMethods = false) + @Import(SimpleExampleService.class) + static class Config { + + @Bean + ExampleServiceCaller serviceCaller(ExampleService service) { + return new ExampleServiceCaller(service); + } + } + +}