Integration Testing with Test Containers and Docker Compose in Springboot 3.0.0

Getting started with integration testing

Schaubild

Embarking on the journey of integration testing is essential for ensuring the seamless collaboration of various components within an application. Positioned between unit tests, which examine individual components, and end-to-end tests, which validate the entire application flow, integration tests play a pivotal role in verifying the harmonious interaction of different elements.

The Challenge

Traditionally, the setup of external dependencies like databases and message brokers for integration tests has been a daunting task. This manual configuration often led to complexities and errors, hindering the efficiency of the testing process. While some opt for mocks as an alternative, these fake environments fall short in truly testing the reality of interactions between components.

The Solution – Testcontainers

Enter Testcontainers, a game-changer in the realm of integration testing. Testcontainers revolutionize the testing landscape by encapsulating external dependencies within containers. This innovative approach simplifies the integration testing process, enhancing efficiency and reducing the likelihood of errors. Notably, these encapsulated dependencies, such as message brokers and databases, mirror those used in production environments, enabling a production-near testing environment.

Key Benefits

The primary advantage of Testcontainers lies in its ability to streamline the orchestration and management of essential services during integration tests. By encapsulating dependencies within containers, Testcontainers eliminates the need for manual setup, allowing developers to effortlessly validate the collaborative behavior of application components.

In the upcoming sections, we will explore the practical aspects of setting up and utilizing Testcontainers. Additionally, we will delve into the integration of Docker Compose, a recent addition in Springboot 3, which further facilitates the setup and utilization of external services within integration tests.

Testcontainers: A Brief Overview

Testcontainers is an open-source framework that offers straightforward and lightweight APIs for initiating integration tests with authentic services encapsulated in Docker containers. By leveraging Testcontainers, you can develop tests that interact with the actual services employed in production thereby eliminating the need for mocks or in-memory services.

Docker Compose: A Brief Overview

Docker Compose is a tool for defining and managing multi-container Docker applications. It allows you to define a multi-container environment in a single file, usually named docker-compose.yml, where you can specify the services, networks, and volumes required for your application.

With Docker Compose, you can describe the configuration of your entire application stack, including the services, their dependencies, and how they should interact with each other. This makes it easier to set up, deploy, and scale complex applications that consist of multiple interconnected containers.

Architectural diagram

To illustrate the concepts mentioned above, I will utilize a Spring Boot application that simulates an order processing system, as represented in the diagram below.

The system is made up of two Spring Boot services:

Order Notification Service: This service is responsible for receiving incoming orders through an API endpoint. Upon receiving orders, it promptly publishes them to a Kafka topic.

Order Processing Service: The Order Service is designed to retrieve and process orders from the Kafka topic on a first-come-first-served basis. The results of the processing phase are then stored in a PostgreSQL database.

Testcontainers and docker-compose will be used to provide the  two external services Kafka and the PostgreSQL database during integration tests.

Prerequisites

To follow along, make sure you have:

  • Java 17, 21 or later
  • Docker Desktop configured on your system.
  • Maven 3.9.6 or later
  • Your favorite code editor or IDE.

The source code can be cloned from  this GitHub repository.

Project setup

The following maven dependencies sets up Testcontainers and docker compose for the project described in the architectural design.

Starting and using Testcontainers in Integration tests

Starting Test Containers

The recommended approach of starting test containers in Integration tests, is to let Springboot manage the lifecycle. This is done by annotating the Test Class with @TestContainer and the external services to be used in this case Kafka and PostgreSQL with @Container annotations respectively. By so doing Springboot will ensure that the containers get started and are ready to be used before any test method is executed. This also ensures that the containers are stopped after the test has been executed. It should be noted that starting and stopping containers can also be done manually, more about that can be seen here.

Note: The configuration above starts the containers with default ports. These and other properties can as well be overridden.

Establishing Connections to Containers

For the application to interact with the Postgres database and Kafka instances running within containers, the connections from the application to the instances must be established. This is achieved by annotating a static method with the @DynamicPropertySource annotation and overriding the connection properties through the DynamicPropertyRegistry.

As of Springboot 3.1, connections to some containers such as the PostgreSQL database can be automatically established via the @ServiceConnection annotation without the need of using the @DynamicPropertySource mentioned above.

With this setup, the different Integration test methods can fully make use of Postgres and Kafka running in Docker containers.

Using Docker Compose in Integration Test

Springboot 3.1 came with a lot of exciting features. One of which is the docker-compose module, which provides the ability to spin up and use services defined in a docker-compose.yml file in integration tests as well as local development environments. All we need to do is add a compose.yml file containing the needed services to the project root directory.

Then add to the application.yml file a lifecycle hook that controls how to start and stop the services defined in the compose.yml file.

The lifecycle-management has three options:

  • start_and_stop: Start Docker Compose if it’s not running and stop it when the JVM exits.
  • start_only: Start Docker Compose if it’s not running.
  • none: Don’t start or stop Docker Compose.

This following property enables the Docker-compose services to be started during test execution.

With this setup, services defined in a single Docker Compose file can completely replace the setup described in the Testcontainers section above. The only limitation at the time of writing this article being that not all services are supported in test mode.

Conclusion

Leveraging Testcontainers and Docker Compose in Spring Boot 3.0.0 brings substantial benefits to integration testing and local development. These tools simplify the setup of complex testing environments, providing consistency, easy configuration, and scalability. By incorporating these practices into your testing strategy, you can build robust and reliable Spring Boot applications with confidence.

Would you like to learn more?

Are you interested and would like to find out more? Talk to us, we look forward to hearing from you!

Kontakt

Francis Mensu
Telefon: +49 (0)40 53302-0
E-Mail: kontakt@cimt-ag.de

cimtAcademy

Jetzt registrieren und keine Veranstaltungen mehr verpassen.​



    Nach oben scrollen