Dynamic property sources for PostgreSQL spring boot tests
Make use of the newly introduced DynamicPropertySource spring annotation in configuring DataSource spring beans required in the tests that make use of testcontainers.
Spring Boot interacts with databases via with data sources
mapped via the spring.datasource in the application.yml file.
In the Spring Boot internals, via
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializationConfiguration, is triggered
the creation of the much needed spring bean instance of type javax.sql.DataSource for JDBC/JPA interactions.
In the context of testing DAO (Data Access Object) classes, it is very common to make use of the testcontainers library.
Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases
Before introducing the annotation @DynamicPropertySource there was needed an
implementation of the ApplicationContextInitializer to introduce dynamicaly the required properties in
the configurable application context for bootstrapping the creation of the data source backed (via
testcontainers library) by a database living in a docker container :
@SpringJUnitConfig(classes = {Application.class}, initializers = {PostgresIntegrationTest.Initializer.class})
@Testcontainers
public class PostgresIntegrationTest {
@Container
public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:12")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
@Autowired
private JdbcTemplate jdbcTemplate;
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
"spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
"spring.datasource.username=" + postgreSQLContainer.getUsername(),
"spring.datasource.password=" + postgreSQLContainer.getPassword()
).applyTo(configurableApplicationContext.getEnvironment());
}
}
@Test
public void demo(){
jdbcTemplate.execute("SELECT 1");
}
}With the introduction of the @DynamicPropertySource there is no need for an extra ApplicationContextInitializer:
@SpringJUnitConfig(classes = {Application.class})
@Testcontainers
public class PostgresIntegrationTest {
@Container
public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:12")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
@Autowired
private JdbcTemplate jdbcTemplate;
@DynamicPropertySource
static void postgresProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
}
@Test
public void demo() {
jdbcTemplate.execute("SELECT 1");
}
}As can be seen from above, the newly introduced @DynamicPropertySource is somehow similar to the
commonly used @TestPropertySource annotation with the mention that it allows the usage of dynamic resources
such as the IP and port assigned to the container (needed in the jdbcUrl in the example above).
Check out the full source code (and corresponding documentation) of the PostgresIntegrationTest.java class.
See more details about the usage of the @DynamicPropertyResource in the Spring framework
documentation.
Sample code
Checkout the github project sample project postgres-spring-boot-dynamicpropertysource and try out the tests via
mvn clean test