Make core auto-configuration tests more independent

Closes gh-44513
This commit is contained in:
Andy Wilkinson 2025-03-05 13:20:31 +00:00
parent 5683d39fd6
commit 85ffbbc384
6 changed files with 93 additions and 56 deletions

View File

@ -29,10 +29,8 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
import org.springframework.boot.context.annotation.ImportCandidates;
import org.springframework.boot.testsupport.classpath.resources.WithResource;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
@ -48,6 +46,15 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Stephane Nicoll
* @author Madhura Bhave
*/
@WithResource(name = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports", content = """
com.example.one.FirstAutoConfiguration
com.example.two.SecondAutoConfiguration
com.example.three.ThirdAutoConfiguration
com.example.four.FourthAutoConfiguration
com.example.five.FifthAutoConfiguration
com.example.six.SixthAutoConfiguration
org.springframework.boot.autoconfigure.AutoConfigurationImportSelectorTests$SeventhAutoConfiguration
""")
class AutoConfigurationImportSelectorTests {
private final TestAutoConfigurationImportSelector importSelector = new TestAutoConfigurationImportSelector();
@ -63,6 +70,7 @@ class AutoConfigurationImportSelectorTests {
this.importSelector.setBeanFactory(this.beanFactory);
this.importSelector.setEnvironment(this.environment);
this.importSelector.setResourceLoader(new DefaultResourceLoader());
this.importSelector.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
}
@Test
@ -77,7 +85,7 @@ class AutoConfigurationImportSelectorTests {
String[] imports = selectImports(EnableAutoConfigurationWithClassExclusions.class);
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
assertThat(this.importSelector.getLastEvent().getExclusions())
.contains(FreeMarkerAutoConfiguration.class.getName());
.contains(SeventhAutoConfiguration.class.getName());
}
@Test
@ -85,7 +93,7 @@ class AutoConfigurationImportSelectorTests {
String[] imports = selectImports(SpringBootApplicationWithClassExclusions.class);
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
assertThat(this.importSelector.getLastEvent().getExclusions())
.contains(FreeMarkerAutoConfiguration.class.getName());
.contains(SeventhAutoConfiguration.class.getName());
}
@Test
@ -93,7 +101,7 @@ class AutoConfigurationImportSelectorTests {
String[] imports = selectImports(EnableAutoConfigurationWithClassNameExclusions.class);
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
assertThat(this.importSelector.getLastEvent().getExclusions())
.contains(MustacheAutoConfiguration.class.getName());
.contains("com.example.one.FirstAutoConfiguration");
}
@Test
@ -101,36 +109,36 @@ class AutoConfigurationImportSelectorTests {
String[] imports = selectImports(SpringBootApplicationWithClassNameExclusions.class);
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
assertThat(this.importSelector.getLastEvent().getExclusions())
.contains(MustacheAutoConfiguration.class.getName());
.contains("com.example.three.ThirdAutoConfiguration");
}
@Test
void propertyExclusionsAreApplied() {
this.environment.setProperty("spring.autoconfigure.exclude", FreeMarkerAutoConfiguration.class.getName());
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.three.ThirdAutoConfiguration");
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
assertThat(this.importSelector.getLastEvent().getExclusions())
.contains(FreeMarkerAutoConfiguration.class.getName());
.contains("com.example.three.ThirdAutoConfiguration");
}
@Test
void severalPropertyExclusionsAreApplied() {
this.environment.setProperty("spring.autoconfigure.exclude",
FreeMarkerAutoConfiguration.class.getName() + "," + MustacheAutoConfiguration.class.getName());
"com.example.two.SecondAutoConfiguration,com.example.four.FourthAutoConfiguration");
testSeveralPropertyExclusionsAreApplied();
}
@Test
void severalPropertyExclusionsAreAppliedWithExtraSpaces() {
this.environment.setProperty("spring.autoconfigure.exclude",
FreeMarkerAutoConfiguration.class.getName() + " , " + MustacheAutoConfiguration.class.getName() + " ");
"com.example.two.SecondAutoConfiguration , com.example.four.FourthAutoConfiguration ");
testSeveralPropertyExclusionsAreApplied();
}
@Test
void severalPropertyYamlExclusionsAreApplied() {
this.environment.setProperty("spring.autoconfigure.exclude[0]", FreeMarkerAutoConfiguration.class.getName());
this.environment.setProperty("spring.autoconfigure.exclude[1]", MustacheAutoConfiguration.class.getName());
this.environment.setProperty("spring.autoconfigure.exclude[0]", "com.example.two.SecondAutoConfiguration");
this.environment.setProperty("spring.autoconfigure.exclude[1]", "com.example.four.FourthAutoConfiguration");
testSeveralPropertyExclusionsAreApplied();
}
@ -138,17 +146,17 @@ class AutoConfigurationImportSelectorTests {
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
assertThat(this.importSelector.getLastEvent().getExclusions())
.contains(FreeMarkerAutoConfiguration.class.getName(), MustacheAutoConfiguration.class.getName());
.contains("com.example.two.SecondAutoConfiguration", "com.example.four.FourthAutoConfiguration");
}
@Test
void combinedExclusionsAreApplied() {
this.environment.setProperty("spring.autoconfigure.exclude", ThymeleafAutoConfiguration.class.getName());
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.one.FirstAutoConfiguration");
String[] imports = selectImports(EnableAutoConfigurationWithClassAndClassNameExclusions.class);
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 3);
assertThat(this.importSelector.getLastEvent().getExclusions()).contains(
FreeMarkerAutoConfiguration.class.getName(), MustacheAutoConfiguration.class.getName(),
ThymeleafAutoConfiguration.class.getName());
"com.example.one.FirstAutoConfiguration", "com.example.five.FifthAutoConfiguration",
SeventhAutoConfiguration.class.getName());
}
@Test
@ -213,7 +221,8 @@ class AutoConfigurationImportSelectorTests {
}
private List<String> getAutoConfigurationClassNames() {
return ImportCandidates.load(AutoConfiguration.class, getClass().getClassLoader()).getCandidates();
return ImportCandidates.load(AutoConfiguration.class, Thread.currentThread().getContextClassLoader())
.getCandidates();
}
private final class TestAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
@ -278,23 +287,23 @@ class AutoConfigurationImportSelectorTests {
}
@EnableAutoConfiguration(exclude = FreeMarkerAutoConfiguration.class)
@EnableAutoConfiguration(exclude = SeventhAutoConfiguration.class)
private final class EnableAutoConfigurationWithClassExclusions {
}
@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class)
@SpringBootApplication(exclude = SeventhAutoConfiguration.class)
private final class SpringBootApplicationWithClassExclusions {
}
@EnableAutoConfiguration(excludeName = "org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration")
@EnableAutoConfiguration(excludeName = "com.example.one.FirstAutoConfiguration")
private final class EnableAutoConfigurationWithClassNameExclusions {
}
@EnableAutoConfiguration(exclude = MustacheAutoConfiguration.class,
excludeName = "org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration")
@EnableAutoConfiguration(exclude = SeventhAutoConfiguration.class,
excludeName = "com.example.five.FifthAutoConfiguration")
private final class EnableAutoConfigurationWithClassAndClassNameExclusions {
}
@ -315,9 +324,14 @@ class AutoConfigurationImportSelectorTests {
}
@SpringBootApplication(excludeName = "org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration")
@SpringBootApplication(excludeName = "com.example.three.ThirdAutoConfiguration")
private final class SpringBootApplicationWithClassNameExclusions {
}
@AutoConfiguration
static class SeventhAutoConfiguration {
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2025 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.
@ -79,7 +79,7 @@ class ImportAutoConfigurationImportSelectorTests {
AnnotationMetadata annotationMetadata = getAnnotationMetadata(FromImportsFile.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsExactly(
"org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration",
"org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelectorTests$ImportedAutoConfiguration",
"org.springframework.boot.autoconfigure.missing.MissingAutoConfiguration");
}
@ -89,8 +89,8 @@ class ImportAutoConfigurationImportSelectorTests {
FromImportsFileIgnoresMissingOptionalClasses.class);
String[] imports = this.importSelector.selectImports(annotationMetadata);
assertThat(imports).containsExactly(
"org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration",
"org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration");
"org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelectorTests$ImportedAutoConfiguration",
"org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelectorTests$AnotherImportedAutoConfiguration");
}
@Test
@ -356,4 +356,14 @@ class ImportAutoConfigurationImportSelectorTests {
}
@AutoConfiguration
static class ImportedAutoConfiguration {
}
@AutoConfiguration
static class AnotherImportedAutoConfiguration {
}
}

View File

@ -16,9 +16,7 @@
package org.springframework.boot.autoconfigure.condition;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
@ -31,6 +29,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport.ConditionAndOutcomes;
import org.springframework.boot.autoconfigure.condition.config.UniqueShortNameAutoConfiguration;
import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportMessage;
import org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
@ -184,24 +183,6 @@ class ConditionEvaluationReportTests {
assertThat(outcomes).hasSize(2);
}
@Test
void duplicateOutcomes() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DuplicateConfig.class);
ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
String autoconfigKey = MultipartAutoConfiguration.class.getName();
ConditionAndOutcomes outcomes = report.getConditionAndOutcomesBySource().get(autoconfigKey);
assertThat(outcomes).isNotNull();
assertThat(outcomes).hasSize(2);
List<String> messages = new ArrayList<>();
for (ConditionAndOutcome outcome : outcomes) {
messages.add(outcome.getOutcome().getMessage());
}
assertThat(messages).anyMatch((message) -> message.contains("@ConditionalOnClass found required classes "
+ "'jakarta.servlet.Servlet', 'org.springframework.web.multipart."
+ "support.StandardServletMultipartResolver', 'jakarta.servlet.MultipartConfigElement'"));
context.close();
}
@Test
void negativeOuterPositiveInnerBean() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@ -220,13 +201,13 @@ class ConditionEvaluationReportTests {
@Test
void reportWhenSameShortNamePresentMoreThanOnceShouldUseFullyQualifiedName() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(WebMvcAutoConfiguration.class,
context.register(UniqueShortNameAutoConfiguration.class,
org.springframework.boot.autoconfigure.condition.config.first.SampleAutoConfiguration.class,
org.springframework.boot.autoconfigure.condition.config.second.SampleAutoConfiguration.class);
context.refresh();
ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
assertThat(report.getConditionAndOutcomesBySource()).containsKeys(
"org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration",
"org.springframework.boot.autoconfigure.condition.config.UniqueShortNameAutoConfiguration",
"org.springframework.boot.autoconfigure.condition.config.first.SampleAutoConfiguration",
"org.springframework.boot.autoconfigure.condition.config.second.SampleAutoConfiguration");
context.close();
@ -235,17 +216,17 @@ class ConditionEvaluationReportTests {
@Test
void reportMessageWhenSameShortNamePresentMoreThanOnceShouldUseFullyQualifiedName() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(WebMvcAutoConfiguration.class,
context.register(UniqueShortNameAutoConfiguration.class,
org.springframework.boot.autoconfigure.condition.config.first.SampleAutoConfiguration.class,
org.springframework.boot.autoconfigure.condition.config.second.SampleAutoConfiguration.class);
context.refresh();
ConditionEvaluationReport report = ConditionEvaluationReport.get(context.getBeanFactory());
String reportMessage = new ConditionEvaluationReportMessage(report).toString();
assertThat(reportMessage).contains("WebMvcAutoConfiguration",
assertThat(reportMessage).contains("UniqueShortNameAutoConfiguration",
"org.springframework.boot.autoconfigure.condition.config.first.SampleAutoConfiguration",
"org.springframework.boot.autoconfigure.condition.config.second.SampleAutoConfiguration");
assertThat(reportMessage)
.doesNotContain("org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration");
.doesNotContain("org.springframework.boot.autoconfigure.condition.config.UniqueShortNameAutoConfiguration");
context.close();
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2012-2025 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.autoconfigure.condition.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionEvaluationReport;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* Uniquely named auto-configuration for {@link ConditionEvaluationReport} tests.
*
* @author Andy Wilkinson
*/
@AutoConfiguration
@ConditionalOnProperty("unique")
public class UniqueShortNameAutoConfiguration {
}

View File

@ -1,2 +1,2 @@
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration
org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelectorTests$ImportedAutoConfiguration
org.springframework.boot.autoconfigure.missing.MissingAutoConfiguration

View File

@ -1,3 +1,3 @@
optional:org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration
optional:org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelectorTests$ImportedAutoConfiguration
optional:org.springframework.boot.autoconfigure.missing.MissingAutoConfiguration
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
org.springframework.boot.autoconfigure.ImportAutoConfigurationImportSelectorTests$AnotherImportedAutoConfiguration