diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/DefaultTestExecutionListenersPostProcessor.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/DefaultTestExecutionListenersPostProcessor.java index 5ba6125a05a..695c3f460f6 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/DefaultTestExecutionListenersPostProcessor.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/DefaultTestExecutionListenersPostProcessor.java @@ -22,7 +22,10 @@ import org.springframework.test.context.ApplicationContextFailureProcessor; import org.springframework.test.context.TestExecutionListener; /** - * Since 3.0.0 this class is not used internally. + * Callback interface trigger from {@link SpringBootTestContextBootstrapper} that can be + * used to post-process the list of default {@link TestExecutionListener + * TestExecutionListeners} to be used by a test. Can be used to add or remove existing + * listeners. * * @author Phillip Webb * @since 1.4.1 diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java index 82155ea7e5c..26f291e719d 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java @@ -38,6 +38,7 @@ import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.env.Environment; +import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfigurationAttributes; import org.springframework.test.context.ContextCustomizer; @@ -47,6 +48,7 @@ import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.TestContext; import org.springframework.test.context.TestContextAnnotationUtils; import org.springframework.test.context.TestContextBootstrapper; +import org.springframework.test.context.TestExecutionListener; import org.springframework.test.context.aot.AotTestAttributes; import org.springframework.test.context.support.DefaultTestContextBootstrapper; import org.springframework.test.context.support.TestPropertySourceUtils; @@ -120,6 +122,18 @@ public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstr return context; } + @Override + @SuppressWarnings("removal") + protected List getDefaultTestExecutionListeners() { + List listeners = new ArrayList<>(super.getDefaultTestExecutionListeners()); + List postProcessors = SpringFactoriesLoader + .loadFactories(DefaultTestExecutionListenersPostProcessor.class, getClass().getClassLoader()); + for (DefaultTestExecutionListenersPostProcessor postProcessor : postProcessors) { + listeners = postProcessor.postProcessDefaultTestExecutionListeners(listeners); + } + return listeners; + } + @Override protected ContextLoader resolveContextLoader(Class testClass, List configAttributesList) { diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java index a8233d05056..9f663e68776 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/SpringBootTestContextBootstrapperIntegrationTests.java @@ -45,6 +45,8 @@ class SpringBootTestContextBootstrapperIntegrationTests { @Autowired private SpringBootTestContextBootstrapperExampleConfig config; + boolean defaultTestExecutionListenersPostProcessorCalled = false; + @Test void findConfigAutomatically() { assertThat(this.config).isNotNull(); @@ -60,6 +62,11 @@ class SpringBootTestContextBootstrapperIntegrationTests { assertThat(this.context.getBean(ExampleBean.class)).isNotNull(); } + @Test + void defaultTestExecutionListenersPostProcessorShouldBeCalled() { + assertThat(this.defaultTestExecutionListenersPostProcessorCalled).isTrue(); + } + @TestConfiguration(proxyBeanMethods = false) static class TestConfig { diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/TestDefaultTestExecutionListenersPostProcessor.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/TestDefaultTestExecutionListenersPostProcessor.java new file mode 100644 index 00000000000..7587c23f93f --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/bootstrap/TestDefaultTestExecutionListenersPostProcessor.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2022 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.boot.test.context.bootstrap; + +import java.util.List; + +import org.springframework.boot.test.context.DefaultTestExecutionListenersPostProcessor; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListener; +import org.springframework.test.context.support.AbstractTestExecutionListener; + +/** + * Test {@link DefaultTestExecutionListenersPostProcessor}. + * + * @author Phillip Webb + */ +@SuppressWarnings("removal") +public class TestDefaultTestExecutionListenersPostProcessor implements DefaultTestExecutionListenersPostProcessor { + + @Override + public List postProcessDefaultTestExecutionListeners(List listeners) { + listeners.add(new ExampleTestExecutionListener()); + return listeners; + } + + static class ExampleTestExecutionListener extends AbstractTestExecutionListener { + + @Override + public void prepareTestInstance(TestContext testContext) throws Exception { + Object testInstance = testContext.getTestInstance(); + if (testInstance instanceof SpringBootTestContextBootstrapperIntegrationTests test) { + test.defaultTestExecutionListenersPostProcessorCalled = true; + } + } + + } + +} diff --git a/spring-boot-project/spring-boot-test/src/test/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-test/src/test/resources/META-INF/spring.factories new file mode 100644 index 00000000000..5eecd4c7e9e --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.test.context.DefaultTestExecutionListenersPostProcessor=\ +org.springframework.boot.test.context.bootstrap.TestDefaultTestExecutionListenersPostProcessor