AR1
AR1

Reputation: 5005

Difference between @PropertySource and @DynamicPropertySource in Spring Framework

The @DynamicPropertySource annotation was added as part of the release 5.2.5 of the Spring Framework. The official documentation says that:

This 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.

There is also a basic example:

@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);
 } }

However, I don't understand... what's the use case for this new annotation when we have @PropertySource that behaves in the same way?

Upvotes: 7

Views: 9437

Answers (1)

AR1
AR1

Reputation: 5005

I eventually found the answer in this Github issue that led to the commit for the @DynamicPropertySource annotation.

The annotation @PropertySource:

Is mainly used to read from property files using Spring Environment interface

Examples of its use can be found in this nice article.

@DynamicPropertySource instead is used to:

make it easier to set configuration properties from something else that's bootstrapped as part of running an integration test.

This helps also setting up integration tests with Testcontainers, getting rid of a lot of boilerplate code.

While the documentation is lacking in this case, a very good article can be found in this blog on the spring.io website. As per the article above, examples are:

@SpringBootTest
@Testcontainers
@ContextConfiguration(initializers = ExampleIntegrationTests.Initializer.class)

class ExampleIntegrationTests {

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

    static class Initializer implements
            ApplicationContextInitializer<ConfigurableApplicationContext> {

        @Override
        public void initialize(ConfigurableApplicationContext context) {
            TestPropertyValues.of("spring.data.neo4j.uri=" + neo4j.getBoltUrl())
                    .applyTo(context.getEnvironment());
        }

    }

}

The @DynamicPropertySource annotation can make the test above clearer, eliminating the boiler plate code as you can see in the example below:

@SpringBootTest
@Testcontainers
class ExampleIntegrationTests {

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

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

}

Upvotes: 15

Related Questions