SpringBoot Integration Testing using TestContainers Starter

Share this post:

One of the many reasons for the huge popularity of Spring and SpringBoot is its 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.

Read Guide to Testing SpringBoot Applications

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 common requirement in SpringBoot applications, so the community built Testcontainers SpringBoot starter to make it easier.

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 the data service library depending on what data service you need. For example, if you want to use a 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, the 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 a 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 provides 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.

Share this post:

Related content