'Integration testing using @SpringBootTest is not picking auto-configuration of library configured using spring.factories

We have a library ,in order to auto-configure the library we use spring.factories file under(src/main/resources/META-INF) which provides classes to auto-configure my library.

Reference : https://docs.spring.io/spring-boot/docs/1.4.0.M3/reference/htmlsingle/#boot-features-custom-starter

I have below configuration in spring.factories file :

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.x.y.PubSubConfig

My understanding is spring.factories is an alternative to configure library and server purpose similar to @SpringBootApplication in a normal application.

I am doing integration using using @SpringBootTest, I expect my context to be configured from configuration classes provided by spring.factories. When i run these tests spring does not identify spring.factories and throws an error

java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test

  • If i annotate my PubSubConfig with SpringBootApplication then my integration tests run perfectly fine but since it is a library I don't wanna do so.
  • If I provide my configuration class specifically using @SpringBootTest(classes = PubSubConfig.class) my tests run fine.

Now I am trying to understand why I need to do either of above dedicatedly as spring.factories is responsible to do my auto-configure



Solution 1:[1]

@SpringBootTest is designed for testing a Spring Boot application. Without any other configuration it looks for a class annotated or meta-annotated with @SpringBootConfiguration. Typically this is your application's main class that is annotated with @SpringBootApplication (which it meta-annotated with @SpringBootConfiguration. @SpringBootApplication is also meta-annotated with @EnableAutoConfiguration so when @SpringBootTest finds the @SpringBootApplication-annotated class auto-configuration is enabled for the tests as it would be when the application itself is being executed.

When you're trying to test your auto-configuration, there's no class annotated with @SpringBootConfiguration so you see this failure:

java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=...) with your test

As you've noted, you could fix the problem by annotating PubSubConfig with @SpringBootApplication but you should not do that as it's a library. @SpringBootTest(classes=PubSubConfig.class) is a better solution as it avoids changing your library's main code, however it's still not ideal as @SpringBootTest is really aimed at testing a Spring Boot application rather than a library that's intended for use within a Spring Boot application.

Rather than using @SpringBootTest, I would recommend using ApplicationContextRunner instead. As it name suggests, it's designed for running an application context. It provides builder methods for configuration auto-configuration and user-configuration that should be used to create the context, setting properties, etc. It also provides an assertable application context that allows you to easily check that the expected beans have and have not been defined. It's used extensively in Spring Boot's own tests for its auto-configuration.

Here's an example taken from Spring Boot's own tests for its DataSource auto-configuration:

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
        .withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class))
        .withPropertyValues("spring.datasource.initialization-mode=never",
                "spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt());

@Test
public void testDefaultDataSourceExists() {
    this.contextRunner.run((context) -> assertThat(context).hasSingleBean(DataSource.class));
}

Solution 2:[2]

In any Spring Boot auto-configuration issue I recommend adding debug: true property to application.yaml. This causes extra debug logs showing which auto-configuration classes were picked up (having criteria that matched) and which were not. In the latter case it shows what conditions didn't match.

Here is a good article about this: https://www.baeldung.com/spring-boot-auto-configuration-report

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Andy Wilkinson
Solution 2 Krzysztof Tomaszewski