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.

Programmer DD
Programmer DD
Programmer DD
Master Spring Boot Auto-Configuration Tests with ApplicationContextRunner

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring BootJUnitauto-configurationApplicationContextRunner
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.