Integration testing in SpringBoot with TestContainers starter
One of the reasons why Spring and Spring Boot are so popular is their good support testing… Can be written as unit tests with Mockito without using the functionality of Spring, and integration with the initialization of the Spring context.
Integration tests may require interaction with external services such as relational databases, NoSQL databases, Kafka, and others. When testing, it is convenient to deploy these services in Docker containers.
Testcontainers
From Testcontainers documentation:
TestContainers is a Java library that supports JUnit tests by providing lightweight, temporary instances for popular databases, web browsers with Selenium, and anything else that can run in a Docker container.
With Testcontainers run Singleton Docker container 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 {
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()
}
}
Since this is used quite often, a starter was created by the community to simplify life – Testcontainers Spring Boot Starter…
Testcontainers SpringBoot Starter
Testcontainers – starter depends on spring-cloud-starter… If your application does not use SpringCloud starters, then you need to add spring-cloud-starter as a test dependency.
org.springframework.cloud
spring-cloud-starter
test
And also add the library for the database. For example, if you want to use Postgresql:
com.playtika.testcontainers
embedded-postgresql
test
When adding embedded-postgresql
the following properties will be available in the environment:
embedded.postgresql.port
embedded.postgresql.host
embedded.postgresql.schema
embedded.postgresql.user
embedded.postgresql.password
They can be used to set up a datasource.
Typically, Docker containers are only used for integration tests, not unit tests. With the help of profiles, we can disable them by default and enable them only for integration tests.
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 you can run integration tests with a profile integration-test through @ActiveProfiles
:
@SpringBootTest
@ActiveProfiles("integration-test")
class UserServiceIntegrationTest {
@Autowired
private UserService userService;
@Test
void shouldGetAllUsers() {
// test userService.getAllUsers()
}
}
You can specify a specific version of the docker image as follows:
src/test/resources/bootstrap-integration-test.properties
embedded.postgresql.dockerImage=postgres:10.7
embedded.postgresql.enabled=true
The Testcontainers starter already provides support for the most popular containers such as Postgresql, MariaDB, MongoDB, Redis, RabbitMQ, Kafka, Elasticsearch and others.
Surprisingly, there is currently no direct support for MySQL. Although there is a simple workaround for this as described here
Refactoring application code in Spring