Integration testing in SpringBoot with TestContainers starter

The translation of the article was prepared in anticipation of the start of the course "Developer on the Spring Framework" .








One of the reasons why Spring and Spring Boot are so popular is their good testing support . You can write both unit tests with Mockito without using Spring functionality, and integration tests 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, transient instances for popular databases, web browsers with Selenium, and anything else that can run in a Docker container.




Using Testcontainers, you can start a 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<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()
    }   

}


Since this is used quite often, a starter was created by the community to simplify life by the community - Testcontainers Spring Boot Starter .



Testcontainers SpringBoot Starter



Testcontainers - The starter depends on the spring-cloud-starter . If your application does not use SpringCloud starters, then you need to add spring-cloud-starter as a test dependency.



<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter</artifactId>
    <scope>test</scope>
</dependency>


And also add the library for the database. For example, if you want to use Postgresql:



<dependency>
    <groupId>com.playtika.testcontainers</groupId>
    <artifactId>embedded-postgresql</artifactId>
    <scope>test</scope>
</dependency>


When added embedded-postgresqlto the environment, the following properties will be available:



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 the integration-test profile using @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 more.

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







All Articles