SpringBoot Integration Testing using TestContainers Starter
One of the many reasons for huge popularity of Spring and SpringBoot is it’s great support for Testing. We can write unit tests using Mockito without requiring any Spring features. And, we can write Integration Tests using Spring testing support by creating Spring ApplicationContext.
While running integration tests we might need to interact with external services like relational databases, NoSQL datastores, Kafka etc. We can spin up those external services as Docker containers and run tests against them.
Testcontainers
From Testcontainers docs:
Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
We can use Testcontainers to spin up a Singleton docker container in a SpringBoot integration test as follows:
@SpringBootTest
@ContextConfiguration(initializers = {UserServiceIntegrationTest.Initializer.class})
class UserServiceIntegrationTest {
private static PostgreSQLContainer sqlContainer;
static {
sqlContainer = new PostgreSQLContainer("postgres:10.7")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
sqlContainer.start();
}
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
"spring.datasource.url=" + sqlContainer.getJdbcUrl(),
"spring.datasource.username=" + sqlContainer.getUsername(),
"spring.datasource.password=" + sqlContainer.getPassword()
).applyTo(configurableApplicationContext.getEnvironment());
}
}
@Autowired
private UserService userService;
@Test
void shouldGetAllUsers() {
// test userService.getAllUsers()
}
}
This is such a very common requirement in SpringBoot applications, so the community built Testcontainers SpringBoot starter to make it more easy.
Testcontainers SpringBoot starter
The Testcontainers SpringBoot starter depends on spring-cloud-starter. If you are NOT already using any SpringCloud starters in the application then include spring-cloud-starter as a test dependency.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
<scope>test</scope>
</dependency>
Now include data service library depending on what data service you need. For example if you want to use Postgresql docker container then add the following dependency:
<dependency>
<groupId>com.playtika.testcontainers</groupId>
<artifactId>embedded-postgresql</artifactId>
<scope>test</scope>
</dependency>
When you add the embedded-postgresql dependency then following properties will be added to the Environment:
- embedded.postgresql.port
- embedded.postgresql.host
- embedded.postgresql.schema
- embedded.postgresql.user
- embedded.postgresql.password
We can use these properties to configure datasource properties for testing.
Usually we want to spin up docker containers for integration tests but not for unit tests. So we can disable it by default and only enable during integration tests by using profile based configuration.
src/test/resources/bootstrap.properties
embedded.postgresql.enabled=false
src/test/resources/bootstrap-integration-test.properties
embedded.postgresql.enabled=true
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://${embedded.postgresql.host}:${embedded.postgresql.port}/${embedded.postgresql.schema}
spring.datasource.username=${embedded.postgresql.user}
spring.datasource.password=${embedded.postgresql.password}
Now we can run integration tests with integration-test profile using @ActiveProfiles as follows:
@SpringBootTest
@ActiveProfiles("integration-test")
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Test
void shouldGetAllUsers() {
// test userService.getAllUsers()
}
}
We may want to use specific version of a docker image, then you can configure it as follows:
src/test/resources/bootstrap-integration-test.properties
embedded.postgresql.dockerImage=postgres:10.7
embedded.postgresql.enabled=true
The Testcontainers SpringBoot starter already provide support for most commonly used containers like Postgresql, MariaDB, MongoDB, Redis, RabbitMQ, Kafka, Elasticsearch etc.
To my surprise there is no direct support for MySQL as of now. But there is a simple workaround for this as described here https://github.com/testcontainers/testcontainers-spring-boot/issues/151.
Related content
- Quirks of Spring's @TestConfiguration
- Creating Yeoman based SpringBoot Generator
- Running your own Spring Initializr and using it from IntelliJ IDEA
- Mastering Spring Boot in 5 Stages
- Thymeleaf Layouts using Fragment Expressions in Spring Boot GraalVM Native Image