From 2b261e6ebdd35cf190bf5f005f3add52ea223729 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Sat, 29 Apr 2023 08:59:48 -0700 Subject: [PATCH] Apply TestTypeExcludeFilter in regular applications Add `ExcludeFilterApplicationContextInitializer` to register the `TestTypeExcludeFilter` for regular applications. Prior to this commit, the filter was only registered using the `ExcludeFilterContextCustomizerFactory` which meant that test components were filtered in tests but not when using `SpringApplication.from` with a test classpath. Fixes gh-35206 --- ...deFilterApplicationContextInitializer.java | 37 ++++++++++++ .../ExcludeFilterContextCustomizer.java | 4 +- .../context/filter/TestTypeExcludeFilter.java | 13 ++++- .../main/resources/META-INF/spring.factories | 4 ++ ...terApplicationContextInitializerTests.java | 58 +++++++++++++++++++ 5 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializer.java create mode 100644 spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializerTests.java diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializer.java new file mode 100644 index 00000000000..2095aef2690 --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012-2023 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.filter; + +import org.springframework.boot.SpringApplication; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * {@link ApplicationContextInitializer} to register the {@link TestTypeExcludeFilter} for + * when {@link SpringApplication#from} is being used with the test classpath. + * + * @author Phillip Webb + */ +class ExcludeFilterApplicationContextInitializer + implements ApplicationContextInitializer { + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + TestTypeExcludeFilter.registerWith(applicationContext.getBeanFactory()); + } + +} diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterContextCustomizer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterContextCustomizer.java index 6b1139ac30c..38351d36803 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterContextCustomizer.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/ExcludeFilterContextCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2023 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. @@ -32,7 +32,7 @@ class ExcludeFilterContextCustomizer implements ContextCustomizer { @Override public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedContextConfiguration) { - context.getBeanFactory().registerSingleton(TestTypeExcludeFilter.class.getName(), new TestTypeExcludeFilter()); + TestTypeExcludeFilter.registerWith(context.getBeanFactory()); } @Override diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/TestTypeExcludeFilter.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/TestTypeExcludeFilter.java index 696d3e29ab3..7420e3195cf 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/TestTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/filter/TestTypeExcludeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2023 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. @@ -18,6 +18,7 @@ package org.springframework.boot.test.context.filter; import java.io.IOException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.boot.test.context.TestComponent; import org.springframework.core.type.classreading.MetadataReader; @@ -32,6 +33,8 @@ import org.springframework.core.type.classreading.MetadataReaderFactory; */ class TestTypeExcludeFilter extends TypeExcludeFilter { + private static final String BEAN_NAME = TestTypeExcludeFilter.class.getName(); + private static final String[] CLASS_ANNOTATIONS = { "org.junit.runner.RunWith", "org.junit.jupiter.api.extension.ExtendWith", "org.junit.platform.commons.annotation.Testable", "org.testng.annotations.Test" }; @@ -39,6 +42,8 @@ class TestTypeExcludeFilter extends TypeExcludeFilter { private static final String[] METHOD_ANNOTATIONS = { "org.junit.Test", "org.junit.platform.commons.annotation.Testable", "org.testng.annotations.Test" }; + private static final TestTypeExcludeFilter INSTANCE = new TestTypeExcludeFilter(); + @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { @@ -91,4 +96,10 @@ class TestTypeExcludeFilter extends TypeExcludeFilter { return false; } + static void registerWith(ConfigurableListableBeanFactory beanFactory) { + if (!beanFactory.containsSingleton(BEAN_NAME)) { + beanFactory.registerSingleton(BEAN_NAME, INSTANCE); + } + } + } diff --git a/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories index 470a48633e3..57c2b6bda7f 100644 --- a/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories @@ -16,3 +16,7 @@ org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener # Environment Post Processors org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.boot.test.web.SpringBootTestRandomPortEnvironmentPostProcessor + +# Application Context Initializers +org.springframework.context.ApplicationContextInitializer=\ +org.springframework.boot.test.context.filter.ExcludeFilterApplicationContextInitializer \ No newline at end of file diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializerTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializerTests.java new file mode 100644 index 00000000000..d221eb8c5cb --- /dev/null +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/filter/ExcludeFilterApplicationContextInitializerTests.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2023 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.filter; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.context.TypeExcludeFilter; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.context.assertj.AssertableApplicationContext; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScan.Filter; +import org.springframework.context.annotation.FilterType; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests {@link ExcludeFilterApplicationContextInitializer}. + * + * @author Phillip Webb + */ +class ExcludeFilterApplicationContextInitializerTests { + + @Test + void testConfigurationIsExcluded() { + SpringApplication application = new SpringApplication(TestApplication.class); + application.setWebApplicationType(WebApplicationType.NONE); + AssertableApplicationContext applicationContext = AssertableApplicationContext.get(() -> application.run()); + assertThat(applicationContext).hasSingleBean(TestApplication.class); + assertThat(applicationContext).doesNotHaveBean(ExcludedTestConfiguration.class); + } + + @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class) }) + static class TestApplication { + + } + + @TestConfiguration(proxyBeanMethods = false) + static class ExcludedTestConfiguration { + + } + +}