Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test @ConfigurationProperties with ApplicationContextRunner from spring-boot-test?

I need to test my autoconfiguration classes that make use of @ConfigurationProperties beans. I'm making use of ApplicationContextRunner as documented in https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-test-autoconfig to make tests faster and avoid starting the servlet container between each variations. However, beans annotated with @AutoconfigurationProperties are not populated with values injected into ApplicationContextRunner.

I suspect that I'm hitting problem similar to https://stackoverflow.com/a/56023100/1484823

@ConfigurationProperties are not managed by the application context you build in tests, although they will be load when the application launches, because you have @EnableConfigurationProperties on your app main class.

How can I enable support for @ConfigurationProperties with ApplicationContextRunner ?

Here is the corresponding code

    @Test
    void ServiceDefinitionMapperPropertiesAreProperlyLoaded() {
        ApplicationContextRunner contextRunner = new ApplicationContextRunner()
            .withConfiguration(AutoConfigurations.of(
                SingleServiceDefinitionAnswerAutoConfig.class,
                DynamicCatalogServiceAutoConfiguration.class
            ))
//          .withPropertyValues(DynamicCatalogProperties.OPT_IN_PROPERTY + "=true") //Not sure why this seems ignored
            .withSystemProperties(DynamicCatalogConstants.OPT_IN_PROPERTY + "=true",
                ServiceDefinitionMapperProperties.PROPERTY_PREFIX
                +ServiceDefinitionMapperProperties.SUFFIX_PROPERTY_KEY+ "=suffix")
        ;
        contextRunner.run(context -> {
            assertThat(context).hasSingleBean(ServiceDefinitionMapperProperties.class);
            ServiceDefinitionMapperProperties serviceDefinitionMapperProperties
                = context.getBean(ServiceDefinitionMapperProperties.class);
            assertThat(serviceDefinitionMapperProperties.getSuffix()).isEqualTo("suffix");
        });
    }

which fails with:

 org.opentest4j.AssertionFailedError: 
Expecting:
 <"">
to be equal to:
 <"suffix">
but was not.
Expected :suffix
Actual   :
<Click to see difference>
    at org.springframework.cloud.appbroker.autoconfigure.DynamicCatalogServiceAutoConfigurationTest
public class DynamicCatalogServiceAutoConfiguration {

[...]

    @Bean
    @ConfigurationProperties(prefix=ServiceDefinitionMapperProperties.PROPERTY_PREFIX, ignoreUnknownFields = false)
    public ServiceDefinitionMapperProperties serviceDefinitionMapperProperties() {
        return new ServiceDefinitionMapperProperties();
    }
[...]
}

Full sources available at https://github.com/orange-cloudfoundry/osb-cmdb-spike/blob/0da641e5f2f811f48b0676a25c8cbe97895168d1/spring-cloud-app-broker-autoconfigure/src/test/java/org/springframework/cloud/appbroker/autoconfigure/DynamicCatalogServiceAutoConfigurationTest.java#L89-L107

ps: I was about to submit an issue to https://github.com/spring-projects/spring-boot/issues to suggest documentation enhancement to warn of such limitation in ApplicationContext, and to ask for ways to turn on support for @ConfigurationProperties. Following guidance at https://raw.githubusercontent.com/spring-projects/spring-boot/master/.github/ISSUE_TEMPLATE.md, I'm first making sure here I'm not misunderstanding the problem.

like image 514
Guillaume Berche Avatar asked Dec 05 '19 08:12

Guillaume Berche


2 Answers

If you want to populate a bean annotated with @ConfigurationProperties class as part of your test, and you normally depend on a configuration class annotated with @EnableConfigurationProperties to populate that bean, then you can create a trivial configuration class just for the test:

@ConfigurationProperties("app")
public class ConfigProps {
    private int meaningOfLife;

    public int getMeaningOfLife() { return meaningOfLife; }
    public void setMeaningOfLife(int meaning) { this.meaningOfLife = meaning; }
}

class ConfigPropsTest {

    private final ApplicationContextRunner runner = new ApplicationContextRunner();

    @EnableConfigurationProperties(ConfigProps.class)
    static class TrivialConfiguration {
    }

    @Test
    void test() {
        runner.withUserConfiguration(TrivialConfiguration.class)
            .withPropertyValues("app.meaning-of-life=42")
            .run(context -> {
                assertEquals(42, context.getBean(ConfigProps.class).getMeaningOfLife());
            });
    }

}

Passing TrivialConfiguration to the ApplicationContextRunner is sufficient to make it create ConfigProps and populate it using the available properties.

like image 191
Kenster Avatar answered Oct 05 '22 19:10

Kenster


As far as I can tell, none of the classes involved in your test enable configuration property binding. As a result, no properties are bound to ServiceDefinitionMapperProperties. You can enable configuration property binding using @EnableConfigurationProperties. A typical place to add it would be on DynamicCatalogServiceAutoConfiguration as its serviceDefinitionMapperProperties bean relies on configuration properties being enabled.

like image 36
Andy Wilkinson Avatar answered Oct 05 '22 19:10

Andy Wilkinson