Testcontainers JDBC Support
Testcontainers is a powerful library designed to simplify the testing of database interactions, especially when dealing with different environments and configurations. It enables the creation and management of Docker containers for testing purposes. This article will delve into Testcontainers’ JDBC support by comparing two approaches: programmatic management of Testcontainer lifecycles and using Testcontainers’ streamlined JDBC configuration.
1. Programmatic Management of Testcontainers
In this approach, we manually manage the lifecycle of the Testcontainer. This gives us fine-grained control but requires more boilerplate code.
Spring Initializr offers a quick and easy way to bootstrap a Spring Boot project with all the necessary dependencies. To integrate PostgreSQL with Testcontainers for integration tests, visit Spring Initializr, configure your project, and then add the following dependencies for a sample setup of a PostgreSQL database:
- Spring Data JPA: Provides JPA support in the Spring Boot project.
- PostgreSQL Driver: Enables PostgreSQL database connectivity.
- Testcontainers: Add this dependency to include Testcontainers in the project.
The generated pom.xml
will contain the required dependencies. Update the pom.xml
as shown below, and here’s an example of how it might look:
<!-- Testcontainers --> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-testcontainers</artifactId> <scope>test</scope> </dependency> <!-- PostgreSQL Driver --> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> </dependency> <!-- Spring Data JPA --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
Next, we will set up the Testcontainer by creating a test class and configuring the PostgreSQL container.
@SpringBootTest class SpringbootTestcontainersApplicationTests { static PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:16-alpine") .withDatabaseName("testdb") .withUsername("test") .withPassword("test"); @Autowired EmployeeRepository employeeRepository; @BeforeAll public static void setUp() { postgresContainer.start(); } @AfterAll public static void tearDown() { if (postgresContainer != null) { postgresContainer.stop(); } } @DynamicPropertySource static void postgresqlProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", () -> postgresContainer.getJdbcUrl()); registry.add("spring.datasource.driverClassName", () -> postgresContainer.getDriverClassName()); registry.add("spring.datasource.username", () -> postgresContainer.getUsername()); registry.add("spring.datasource.password", () -> postgresContainer.getPassword()); } @Test public void testMySQLConnection() throws Exception { employeeRepository.save(new Employee("Josh Fish", "josh@jcg.com")); assertThat(employeeRepository.findAll()) .hasSize(1).first() .extracting(Employee::getEmail) .isEqualTo("josh@jcg.com"); System.out.println("" + postgresContainer.getLogs()); } }
In this example, the PostgreSQLContainer
is started before each test and stopped afterwards. The method postgresqlProperties
is annotated with @DynamicPropertySource
, which allows us to dynamically set the Spring datasource properties (url
, driverClassName
, username
, and password
) at runtime, based on the properties of the PostgreSQL container.
By using @DynamicPropertySource
, we avoid hardcoding the JDBC connection details, especially the port. This ensures that the test environment is flexible and less prone to errors due to port conflicts. This dynamic setup also allows for smoother integration with other services and containers that might be running concurrently.
2. Testcontainers JDBC Support
While the programmatic approach is powerful, Testcontainers also offers a more streamlined way to manage containers in tests through JDBC support. By using a simple configuration property, we can simplify the setup and allow the framework to handle the lifecycle management for us.
In our application.properties
or application.yml
file, we need to specify the Testcontainers JDBC URL to enable automatic container management for the database:
spring.datasource.url=jdbc:tc:postgresql:16-alpine:///testdb spring.jpa.hibernate.ddl-auto=create
By leveraging the jdbc:tc:
prefix in the JDBC URL (spring.datasource.url
), Testcontainers automatically handles the lifecycle of the PostgreSQL container. The container starts up before the test and shuts down afterwards without any manual intervention.
Now, we can write a simple Spring Boot test that uses this configuration:
@SpringBootTest public class CustomTestcontainerTests { @Autowired EmployeeRepository employeeRepository; @Test public void testPostgreSQLConnection() { employeeRepository.save(new Employee("Josh Fish", "josh@jcg.com")); assertThat(employeeRepository.findAll()) .hasSize(1).first() .extracting(Employee::getEmail) .isEqualTo("josh@jcg.com"); } }
This method significantly reduces the amount of setup code, making it an ideal choice for quick and straightforward tests.
3. Comparison of the Two Approaches
Feature | Programmatic Lifecycle Management | Testcontainers JDBC Support |
---|---|---|
Control over Lifecycle | High control; manual start/stop of containers | Low control; lifecycle managed automatically |
Setup Complexity | Requires setup and teardown methods | Minimal setup; just a JDBC URL configuration |
Integration with Spring | Requires manual configuration | Seamless integration with Spring Boot |
Use Case | Ideal for complex scenarios needing control | Ideal for simple, quick integration tests |
4. Conclusion
In this article, we explored two different approaches to using Testcontainers JDBC support for managing Docker containers in your integration tests. We started by examining how to programmatically manage the lifecycle of containers, offering full control and flexibility over container management. We then compared this with a more streamlined approach, utilizing Testcontainers JDBC support to simplify the setup through a single configuration property.
5. Download the Source Code
This article explores the JDBC integration features of Testcontainers.
You can download the full source code of this example here: Testcontainers JDBC integration