Master Spring Boot Auto-Configuration Tests with ApplicationContextRunner
This tutorial explains how to use Spring Boot's ApplicationContextRunner to simplify testing of auto‑configuration classes, covering class, bean, and property conditions with concrete code examples and assertions for both present and missing scenarios.
1. Overview
Auto‑configuration is a key feature of Spring Boot, but testing it can be tricky.
In the following sections we demonstrate how ApplicationContextRunner simplifies auto‑configuration testing.
2. Testing Auto-Configuration Solutions
ApplicationContextRunner is a utility class that runs an ApplicationContext and provides AssertJ‑style assertions. It is best used as a field in test classes to share configuration, then customized per test.
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner();Let's showcase its magic with several test cases.
2.1. Test Class Condition
We test auto‑configuration classes annotated with @ConditionalOnClass and @ConditionalOnMissingClass.
@Configuration
@ConditionalOnClass(ConditionalOnClassIntegrationTest.class)
protected static class ConditionalOnClassConfiguration {
@Bean
public String created() {
return "This is created when ConditionalOnClassIntegrationTest is present on the classpath";
}
}
@Configuration
@ConditionalOnMissingClass("com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest")
protected static class ConditionalOnMissingClassConfiguration {
@Bean
public String missed() {
return "This is missed when ConditionalOnClassIntegrationTest is present on the classpath";
}
}We verify that the appropriate beans are instantiated or skipped based on the presence of the class.
ApplicationContextRunner provides withUserConfiguration to supply auto‑configuration for each test.
The run method accepts a ContextConsumer that applies assertions; the context is automatically closed after the test.
@Test
public void whenDependentClassIsPresent_thenBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
.run(context -> {
assertThat(context).hasBean("created");
assertThat(context.getBean("created"))
.isEqualTo("This is created when ConditionalOnClassIntegrationTest is present on the classpath");
});
}
@Test
public void whenDependentClassIsPresent_thenBeanMissing() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
.run(context -> {
assertThat(context).doesNotHaveBean("missed");
});
}When the class is absent, we use FilteredClassLoader to simulate the missing class.
@Test
public void whenDependentClassIsNotPresent_thenBeanMissing() {
this.contextRunner.withUserConfiguration(ConditionalOnClassConfiguration.class)
.withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
.run(context -> {
assertThat(context).doesNotHaveBean("created");
assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
});
}
@Test
public void whenDependentClassIsNotPresent_thenBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class)
.withClassLoader(new FilteredClassLoader(ConditionalOnClassIntegrationTest.class))
.run(context -> {
assertThat(context).hasBean("missed");
assertThat(context.getBean("missed"))
.isEqualTo("This is missed when ConditionalOnClassIntegrationTest is present on the classpath");
assertThat(context).doesNotHaveBean(ConditionalOnClassIntegrationTest.class);
});
}2.2. Test Bean Condition
We now examine @ConditionalOnBean and @ConditionalOnMissingBean.
@Configuration
protected static class BasicConfiguration {
@Bean
public String created() {
return "This is always created";
}
}
@Configuration
@ConditionalOnBean(name = "created")
protected static class ConditionalOnBeanConfiguration {
@Bean
public String createOnBean() {
return "This is created when bean (name=created) is present";
}
}
@Configuration
@ConditionalOnMissingBean(name = "created")
protected static class ConditionalOnMissingBeanConfiguration {
@Bean
public String createOnMissingBean() {
return "This is created when bean (name=created) is missing";
}
}Tests use withUserConfiguration to verify bean creation or omission under the specified conditions.
@Test
public void whenDependentBeanIsPresent_thenConditionalBeanCreated() {
this.contextRunner.withUserConfiguration(BasicConfiguration.class, ConditionalOnBeanConfiguration.class)
// assertions omitted for brevity
}
@Test
public void whenDependentBeanIsNotPresent_thenConditionalMissingBeanCreated() {
this.contextRunner.withUserConfiguration(ConditionalOnMissingBeanConfiguration.class)
// assertions omitted for brevity
}2.3. Test Property Condition
We test auto‑configuration classes that depend on @ConditionalOnProperty.
com.baeldung.service=custom @Configuration
@TestPropertySource("classpath:ConditionalOnPropertyTest.properties")
protected static class SimpleServiceConfiguration {
@Bean
@ConditionalOnProperty(name = "com.baeldung.service", havingValue = "default")
@ConditionalOnMissingBean
public DefaultService defaultService() {
return new DefaultService();
}
@Bean
@ConditionalOnProperty(name = "com.baeldung.service", havingValue = "custom")
@ConditionalOnMissingBean
public CustomService customService() {
return new CustomService();
}
}Using withPropertyValues we override the property to test both scenarios.
@Test
public void whenGivenCustomPropertyValue_thenCustomServiceCreated() {
this.contextRunner.withPropertyValues("com.baeldung.service=custom")
.withUserConfiguration(SimpleServiceConfiguration.class)
.run(context -> {
assertThat(context).hasBean("customService");
SimpleService simpleService = context.getBean(CustomService.class);
assertThat(simpleService.serve()).isEqualTo("Custom Service");
assertThat(context).doesNotHaveBean("defaultService");
});
}
@Test
public void whenGivenDefaultPropertyValue_thenDefaultServiceCreated() {
this.contextRunner.withPropertyValues("com.baeldung.service=default")
.withUserConfiguration(SimpleServiceConfiguration.class)
.run(context -> {
assertThat(context).hasBean("defaultService");
SimpleService simpleService = context.getBean(DefaultService.class);
assertThat(simpleService.serve()).isEqualTo("Default Service");
assertThat(context).doesNotHaveBean("customService");
});
}3. Conclusion
This tutorial demonstrated how to use ApplicationContextRunner to run a customized ApplicationContext and apply assertions, covering the most common scenarios for class, bean, and property conditions. For non‑web tests use ApplicationContextRunner; for servlet‑based or reactive web tests consider WebApplicationContextRunner or ReactiveWebApplicationContextRunner respectively.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
