It’s well known that auto-configuration is one of the key features in Spring Boot, but testing auto-configuration scenarios can be tricky.
In the following sections, we’ll show how ApplicationContextRunner simplifies auto-configuration testing.
2 Test Auto-Configuration Scenarios
ApplicationContextRunner is a utility class which runs the ApplicationContext and provides AssertJ style assertions. It’s best used as a field in test class for shared configuration and we make customizations in each test afterward:
@Configuration @ConditionalOnClass(ConditionalOnClassIntegrationTest.class) protectedstaticclassConditionalOnClassConfiguration { @Bean public String created() { return"This is created when ConditionalOnClassIntegrationTest " + "is present on the classpath"; } }
@Configuration @ConditionalOnMissingClass( "com.baeldung.autoconfiguration.ConditionalOnClassIntegrationTest" ) protectedstaticclassConditionalOnMissingClassConfiguration { @Bean public String missed() { return"This is missed when ConditionalOnClassIntegrationTest " + "is present on the classpath"; } }
We’d like to test whether the auto-configuration properly instantiates or skips the created and missed beans given expected conditions.
ApplicationContextRunner gives us the withUserConfiguration method where we can provide an auto-configuration on demand to customize the ApplicationContext for each test.
The run method takes a ContextConsumer as a parameter which applies the assertions to the context. The ApplicationContext will be closed automatically when the test exits:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
@Test publicvoidwhenDependentClassIsPresent_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"); }); }
Through the preceding example, we see the simpleness of testing the scenarios in which a certain class is present on the classpath. But how are we going to test the converse, when the class is absent on the classpath?
This is where FilteredClassLoader kicks in. It’s used to filter specified classes on the classpath at runtime:
@Test publicvoidwhenDependentClassIsNotPresent_thenBeanCreated() { this.contextRunner.withUserConfiguration(ConditionalOnMissingClassConfiguration.class) .withClassLoader(newFilteredClassLoader(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’ve just looked at testing @ConditionalOnClass and @ConditionalOnMissingClass annotations, now let’s see what things look like when we are using @ConditionalOnBean and @ConditionalOnMissingBean annotations.
To make a start, we similarly need a few auto-configuration classes:
@Configuration protectedstaticclassBasicConfiguration { @Bean public String created() { return"This is always created"; } } @Configuration @ConditionalOnBean(name = "created") protectedstaticclassConditionalOnBeanConfiguration { @Bean public String createOnBean() { return"This is created when bean (name=created) is present"; } } @Configuration @ConditionalOnMissingBean(name = "created") protectedstaticclassConditionalOnMissingBeanConfiguration { @Bean public String createOnMissingBean() { return"This is created when bean (name=created) is missing"; } }
Then, we’d call the withUserConfiguration method like the preceding section and send in our custom configuration class to test if the auto-configuration appropriately instantiates or skips createOnBean or createOnMissingBean beans in different conditions:
To sum up, this tutorial just showed how to use ApplicationContextRunner to run the ApplicationContext with customizations and apply assertions.
We covered the most frequently used scenarios in here instead of an exhaustive list of how to customize the ApplicationContext.
In the meantime, please bear in mind that the ApplicationConetxtRunner is for non-web applications, so consider WebApplicationContextRunner for servlet-based web applications and ReactiveWebApplicationContextRunner for reactive web applications.
The source code for this tutorial can be found over on GitHub.