Quarkus et Testcontainers
Si vous ne connaissait pas Quarkus, voici un article d’introduction : Zoom sur Quarkus.
Quarkus offre un support des Tests Unitaires (TU) avec JUnit 5 via l’annotation @QuarkusTest
, la documentation du support des TU de Quarkus peut être trouvé ici.
Voici un exemple de TU tiré du Quickstart Hibernate ORM :
@QuarkusTest public class FruitsEndpointTest { @Test public void testListAllFruits() { given() .when().get("/fruits") .then() .statusCode(200) .body( containsString("Cherry"), containsString("Apple"), containsString("Banana") ); } }
Classiquement, on peut exécuter des tests en utilisant une base de données embarquée, Quarkus supporte la BDD H2 pour ça. Mais si on veut que nos tests tournent dans un environnement qui ressemble le plus à la production, l’utilisation de la même base de données est idéale.
On voudrait donc pouvoir utiliser une base de données Postgres pour exécuter nos tests.
Testcontainers est une librairie qui permet de facilement démarrer un conteneur Docker au sein de vos TUs. Il fournit une extension JUnit 5 et une extension permettant de facilement lancer des bases de données. Son utilisation serait donc idéale pour lancer une base de données Postgres pour nos TUs.
L’intégration classique de Testcontainers se fait via l’annotation @Testcontainers
qui permet de démarrer l’extension JUnit 5 pour Testcontainers et permet de démarrer des conteneurs via @Container
, voir la documentation de l’extension Junit 5 de Testcontainers.
Le soucis c’est que les deux extensions, celle de Quarkus et celle de Testcontainers, ne fonctionne pas très bien ensemble car Quarkus va démarrer avant Testcontainers et le conteneur ne sera pas démarré avant le démarrage de l’application Quarkus. La base de données ne sera donc pas démarré lors du démarrage de votre application et celle-ci ne réussira donc pas à démarrer.
Avec JUnit 5, on devrait pouvoir ordonner les extensions entre elles via @ExtendWith
mais ce n’est pas possible d’utiliser cette annotation avec Testcontainers (plus d’info ici).
La solution ? L’utilisation d’une URL JDBC modifiée pour Testcontainers (documentée ici).
Pour notre exemple il faut tout d’abord ajouter les dépendances Testcontainers à notre 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>
Ensuite, ajouter l’annotation @Testcontainers
à notre FruitsEndpointTest
.
Pour finir configurer notre datasource via notre application.properties
en utilisant une URL JDBC modifiée pour 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, il faut alors spécifier le dialect Hibernate car la sélection automatique de dialect ne fonctionne pas quand l’URL JDBC est modifiée.
Une fois tout ça configuré, lorsque Quarkus initialisera sa datasource, Testcontainers démarrera automatiquement un conteneur Postgres et vous verrez les lignes suivantes dans vos 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