Dockerize your integration tests

Our Elasticsearch server is ready to be used.

To start it up, we simply have to execute the start method:container.

start();At startup, Testcontainers will run a bunch of checks like the docker version or the connection to the registered Docker Registry.

This can be blocking if you are working behind a company proxy, so it’s possible to disable those checks by creating the file testcontainers.

properties in the tests resources directory with this content:check.

disable=trueAt last, we can stop our container with the method stop.

container.

stop();This will stop the container and also remove the attached volume.

This is great because it prevents having dangling volumes.

During TestsOne great strength of Testcontainers is its integration with JUnit framework.

In fact, GenericContainer objects are JUnit rules.

It means that their lifecycle is directly bound to the test lifecycle.

Thereby, by using the @Rule or @ClassRuleJUnit annotations, our containers will be initialized before the test start-up and stopped at the end of the tests execution.

Nevertheless, it means that Testcontainers will come with a JUnit 4 dependency and it can be annoying if your tests run with JUnit 5.

Indeed, JUnit has replaced the Rule concept with Extension.

Since the version 1.

10.

0 released on November 2018, Testcontainers supports now JUnit 5 and it’s possible to use extensions with the help of @Testcontainersand @Containerannotations from the dedicated library junit-jupiter:<dependency> <groupId>testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>1.

10.

2</version></dependency>Preconfigured containerLike Docker, Testcontainers ecosystem is very rich.

You can find preconfigured containers like MySQL, PostgreSQL, Oracle database, Kafka, Neo4j, Elasticsearch, etc.

@Rulepublic KafkaContainer kafka = new KafkaContainer();You can browse the list directly from maven repository.

A concrete caseLet’s see a concrete example of using Testscontainers with the Spring PetClinic application.

It’s a demonstrating project based on several Spring components like Spring Boot, Spring MVC and Spring JPA.

This application aims at managing a pet clinic with pets, pet owners and vets.

The controller layer exposes HTTP endpoints to create and read entities.

Then, the persistence layer communicates with a relational database.

The application can be configured to communicate with a HSQLDB or a MySQL database.

The persistence layer is tested with integration tests and those uses an in-memory HSQL database while the persistence layer itself uses a MySQL database.

RequirementsFirst, we have to install Docker on the machine which is going to execute tests.

Then, we need to add the Testcontainers dependency to the project.

In this case, we simply add the following to the pom.

xml file:<dependency> <groupId>org.

testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>1.

10.

2</version> <scope>test</scope></dependency>Database configurationThe default database configuration is done in the application.

properties file.

database=hsqldbspring.

datasource.

schema=classpath*:db/${database}/schema.

sqlspring.

datasource.

data=classpath*:db/${database}/data.

sqlAs we can see, this is an in-memory HSQLDB database initialized with a schema from the schema.

sql file.

Then, the database is populated with the data.

sql file.

This is the default project configuration.

We need to create application-test.

properties file to configure a connection to a MySQL database.

spring.

datasource.

url=jdbc:mysql://localhost/petclinicspring.

datasource.

username=petclinicspring.

datasource.

password=petclinicspring.

datasource.

driver-class-name=com.

mysql.

jdbc.

DriverNext, let’s take the test class ClinicServiceTests.

java.

This class contains all integration tests for the persistence layer.

First of all, we need to change Spring test configuration to ensure that the tests will use our database connection.

The TestPropertySource annotation enables to load our file application-test.

properties and AutoConfigureTestDatabase with the NONE value prevents Spring from creating an embedded database.

MySQL containerLet’s create a MySQL database that matches requirements from our tests.

In this instance, we use the ability from Testcontainers to create a Docker image from a Dockerfile created on the fly.

As a first step, we have pulled a MySQL official image from Docker Hub:Now, we have to create our database and the connection’s user.

This is done by using environment variables from the Docker image.

Next, we have to create a database schema and populate the database.

From the image documentation, the directory /docker-entrypoint-initdb.

d is scanned at startup and all files with .

sh, .

sql et .

sql.

gz extension are executed.

So, we just have to put our files schema.

sql and data.

sql in this directory.

By using withClasspathResourceMapping, the files schema.

sql and data.

sql are put on the classpath into the container as a volume.

Then, we can access it into our Dockerfile construction.

One last thing, we have to expose the default MySQL port: 3306.

Unfortunately, we can’t directly set port bindings with the method setPortBindings.

We have to customize the container on creation with the method withCreateContainerCmdModifier.

Finally, we are waiting for the listening port to ensure that our container is up.

Voilà!.With few lines of code, we have easily set a MySQL database for our tests without having to manage the container lifecycle.

The @ClassRule annotation makes our container starting once for all the tests.

You might be wondering: have we extend the test execution time?.In fact, it only takes 907 ms with a Docker container against 860 ms with a HSQLDB in-memory database.

The source code shown in this section is available on github.

A big thanks to Sonyth, Sebastien, Laurent, Louis and Nicolas for their time and proofreading.

.. More details

Leave a Reply