find in path

Dynamic property sources for PostgreSQL spring boot tests

2020-03-29testcontainerspostgresqlspring-boot

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