Quarkus and Testcontainers

Quarkus and Testcontainers

If you did not know Quarkus, here is an introductory article: Zoom sur Quarkus (french).

Quarkus offers Unit Test (TU) support with JUnit 5 via the @QuarkusTest annotation, documentation for Quarkus TU support can be found here.

Here is an example of a TU from the Hibernate ORM Quickstart :

@QuarkusTest
public class FruitsEndpointTest {

    @Test
    public void testListAllFruits() {
        given()
              .when().get("/fruits")
              .then()
              .statusCode(200)
              .body(
                    containsString("Cherry"),
                    containsString("Apple"),
                    containsString("Banana")
                    );
    }
}

Conventionally, you can run tests using an embedded database, Quarkus supports the H2 BDD for that. But if we want our tests to run in an environment as close as possible to production, the use of the same database is better.

We would therefore like to be able to use a Postgres database to run our tests.

Testcontainers is a library that allows you to easily start a Docker container within your TUs. It provides a JUnit 5 extension and an extension to easily launch databases. Its use would therefore be ideal for launching a Postgres database for our TUs.

The classical integration of Testcontainers is done via the @Testcontainers annotation which allows to start the JUnit 5 extension for Testcontainers and allows to start containers via @Container, see the documentation of the Junit 5 extension of Testcontainers.

The issue is that the two extensions, the Quarkus one and the Testcontainers one, do not work very well together because Quarkus will start before Testcontainers and the container will not be started before the Quarkus application starts. The database will therefore not be started when your application starts, so your application will fails to start.

With JUnit 5, we should be able to order the extensions between them via @ExtendWith but it is not possible to use this annotation with Testcontainers (more info here).

The solution ? Using a modified JDBC URL for Testcontainers (documented here ).

For our example we must first add the Testcontainers dependencies to our pom.xml .

    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>testcontainers</artifactId>
      <version>${testcontainers.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>junit-jupiter</artifactId>
      <version>${testcontainers.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.testcontainers</groupId>
      <artifactId>postgresql</artifactId>
      <version>${testcontainers.version}</version>
      <scope>test</scope>
    </dependency>

Then add the annotation @Testcontainers to our FruitsEndpointTest .

Finally configure our datasource via our application.properties using a modified JDBC URL for Testcontainers:

quarkus.datasource.url = jdbc:tc:postgresql:11.3://hostname/mydatabase
quarkus.datasource.driver = org.testcontainers.jdbc.ContainerDatabaseDriver
quarkus.hibernate-orm.dialect = org.hibernate.dialect.PostgreSQLDialect

Attention, you must then specify the Hibernate dialect because the automatic dialect selection does not work when the JDBC URL is modified.

Once all this is configured, when Quarkus initializes its datasource, Testcontainers will automatically start a Postgres container and you will see the following lines in your logs:

2020-02-17 15:03:20,078 INFO  [org.tes.doc.DockerClientProviderStrategy] (Agroal_5751340441) Loaded org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy from ~/.testcontainers.properties, will try it first
2020-02-17 15:03:20,283 INFO  [org.tes.doc.EnvironmentAndSystemPropertyClientProviderStrategy] (Agroal_5751340441) Found docker client settings from environment
2020-02-17 15:03:20,283 INFO  [org.tes.doc.DockerClientProviderStrategy] (Agroal_5751340441) Found Docker environment with Environment variables, system properties and defaults. Resolved dockerHost=unix:///var/run/docker.sock
2020-02-17 15:03:20,374 INFO  [org.tes.DockerClientFactory] (Agroal_5751340441) Docker host IP address is localhost
2020-02-17 15:03:20,390 INFO  [org.tes.DockerClientFactory] (Agroal_5751340441) Connected to docker: 
[...]
2020-02-17 15:03:20,958 INFO  [org.tes.DockerClientFactory] (Agroal_5751340441) Ryuk started - will monitor and terminate Testcontainers containers on JVM exit
2020-02-17 15:03:20,970 INFO  [🐳 .3]] (Agroal_5751340441) Creating container for image: postgres:11.3
2020-02-17 15:03:21,009 INFO  [🐳 .3]] (Agroal_5751340441) Starting container with ID: c40f75e48f238f2aafe14899adae0afd3a3948396c19247aff64811166d4b057
2020-02-17 15:03:21,390 INFO  [🐳 .3]] (Agroal_5751340441) Container postgres:11.3 is starting: c40f75e48f238f2aafe14899adae0afd3a3948396c19247aff64811166d4b057
2020-02-17 15:03:23,038 INFO  [🐳 .3]] (Agroal_5751340441) Container postgres:11.3 started in PT2.067585S

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.