2020-03-29

Spring @DynamicPropertySource example

The TestContext framework provides support for dynamic property sources via the @DynamicPropertySource annotation. This annotation can be used in integration tests that need to add properties with dynamic values to the set of PropertySources in the Environment for the ApplicationContext loaded for the integration test.

The @DynamicPropertySource annotation and its supporting infrastructure were originally designed to allow properties from Testcontainers based tests to be exposed easily to Spring integration tests. However, this feature may also be used with any form of external resource whose lifecycle is maintained outside the test’s ApplicationContext.

In contrast to the @TestPropertySource annotation that is applied at the class level, @DynamicPropertySource must be applied to a static method that accepts a single DynamicPropertyRegistry argument which is used to add name-value pairs to the Environment. Values are dynamic and provided via a Supplier which is only invoked when the property is resolved. Typically, method references are used to supply values, as can be seen in the following example which uses the Testcontainers project to manage a Redis container outside of the Spring ApplicationContext. The IP address and port of the managed Redis container are made available to components within the test’s ApplicationContext via the redis.host and redis.port properties. These properties can be injected into Spring-managed components via @Value("${redis.host}") and @Value("${redis.port}"), respectively.

@SpringJUnitConfig(/* ... */)
@Testcontainers
class ExampleIntegrationTests {

    @Container
    static RedisContainer redis = new RedisContainer();

    @DynamicPropertySource
    static void redisProperties(DynamicPropertyRegistry registry) {
        registry.add("redis.host", redis::getContainerIpAddress);
        registry.add("redis.port", redis::getMappedPort);
    }

    // tests ...

}


The use of @ContextConfiguration and the ApplicationContextInitializer can be replaced with a static @DynamicPropertySource method that serves the same purpose. If we make these changes to the integration test class shown above, it now looks like the following:

@SpringBootTest
@Testcontainers
class ExampleIntegrationTests {

    @Container
    static Neo4jContainer<?> neo4j = new Neo4jContainer<>();

    @DynamicPropertySource
    static void neo4jProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.data.neo4j.uri", neo4j::getBoltUrl);
    }

}

No comments:

Post a Comment