- • Quickstart
- • Documentation
- • Documentation DevOps
- • Next Steps
- › Spring Boot
- • Spring Data
- • Spring Data MongoDB
- • Spring Security
- • Frontend
- • Multi-Module
Last updated: 2023-06-09
Adding Integration Tests in Spring Boot with Testcontainers
Use the free Bootify Builder for starting your next Spring Boot app in no time - with custom database schema and best practices applied. Testcontainers setup together with generated test data is available in the Professional plan.
Good software always includes automated tests to detect errors during the creation and modification of the code. In this tutorial integration tests will be added to our Spring Boot application, connecting to the 'real' database using the Testcontainers library.
An integration test is meant to test the interaction of multiple parts of the application. So in our Spring Boot context it would be great to examine the running app including the database (for example MySQL or PostgreSQL). An embedded database would not be optimal for this, as there can be significant differences to the actual database, and therefore certain features may not work or errors may be missed.
For this purpose, there is the Testcontainers library to start a Docker image of exactly the database that is used by the developer and later in production. To write an integration test for our application, we start by adding the required dependencies.
Adding dependencies to our build.gradle
spring-boot-starter-test we also add
org.springframework.boot:spring-boot-testcontainers. Since Spring Boot
3.1.0 there is first-class support for Testcontainers, making our live even easier. With
org.testcontainers:mysql we add the container class for the actual database we are using - in our case we choose MySQL.
Starting the application context
To centralize the recurring logic of our test classes, we first create an abstract base class from which our IT classes will inherit later on. The first version of this class looks like this:
First version of BaseIT.java
By using the
@SpringBootTest annotation, Spring Boot will load the full application context and thus enables us to make good integration tests. With
@ActiveProfiles we define that the application is started with the profile
"it". If needed we can use this profile to activate special settings in our code.
By setting the variable
RANDOM_PORT the application will be started under a random and available port, so during testing there shouldn't be any conflict.
MockMvc is configured automatically with the annotation
AutoConfigureMockMvc - there are a lot of useful helpers included for calling our endpoints.
Mocking the database
Now we want to configure a database that is exclusively available for our tests. For this we extend our
BaseIT.java with the following code.
Testcontainers setup to provide MySQL as a container
The proper container from Testcontainers is initialized with the given image
"mysql:8.2", in our case the newest version of MySQL. Ideally this should exactly match the version that is also used in production.
Originally a special method annotated with
@DynamicPropertySource was required to provide the connection credentials to the application context. With Spring Boot
3.1.0 that's not required anymore for most databases and simply adding
@ServiceConnection will do exactly that.
Within the static block we are setting the reuse parameter, so that the container will continue running until we manually stop it. Usually that's not causing issues and makes testing the application faster - the Docker container can be stopped manually if required. To enable reuse, the file
/<usersdir>/.testcontainers.properties in your environment must be extended by the entry
Finally we are starting the container with a call to
start(). This approach ensures that all extending classes will reuse the same container. Depending on how we initialize our database schema - for example with Flyway or Liquibase - this will be automatically applied to our test database within the container during context startup.
Running the tests
With this preparation, we can write our first test class. Let's assume that the following
@RestController already exists. The referenced service returns all entries that exist in the "Test" table.
Example endpoint in our Spring Boot app
We can now create a class
TestControllerIT that extends our abstract base class. The test method sends a GET request to the existing endpoint using
MockMvc. We're asserting that the HTTP status and the response JSON are matching our expectations.
TestControllerIT of our Spring Boot app
Even though our database schema is already initialized, we still lack explicit test data that we need for our test. For this we use the Spring annotation
@Sql, which executes two scripts and thus puts our database into a known state. In our case, there is now exactly one entry in the Test table that we expect as a result.
Wiping out all data with src/test/resources/data/clearAll.sql
Create a single table entry with testData.sql
Docker must be available and running when the test is executed. When we run the test for the first time we have to wait a bit for our container to be available. It may be useful to already cache the image beforehand using
docker pull mysql:8.2 to avoid issues with the download. Our test should now go through without errors.
With the setup described, we have created a way to check the behavior of our application including the database from a high level. With this we have a very useful addition to our unit tests. Testcontainers also offers support for other services such as RabbitMQ, so we can flexibly add such containers to BaseIT as needed.
In the Professional plan, Bootify offers the option to activate Testcontainers. This initializes the Spring Boot application including the described setup, depending on the selected database. It also generates the IT classes and scripts according to the tables and REST controllers created.