Test des applications web Quarkus : Tests de composants et d’intégration

Points clés à retenir

    &#13 ;
  • Quarkus est un framework Java complet, natif de Kubernetes, conçu pour les machines virtuelles Java (JVM) et la compilation native.
  • &#13 ;

  • Au lieu de réinventer la roue, Quarkus utilise des cadres d’entreprise bien connus, soutenus par des normes/spécifications, et les rend compilables en binaire à l’aide de Graal VM.
  • &#13 ;

  • Nous commençons ici par une vérification complète de la logique d’une application type, puis nous voyons étape par étape comment nous pouvons réduire la portée du test en utilisant certaines des installations de test de Quarkus.
  • &#13 ;

  • Bien que le processus de compilation native doive fonctionner en permanence, il est également vrai que lorsqu’un exécutable natif est exécuté, quelques problèmes peuvent survenir. C’est pourquoi il est judicieux d’effectuer des tests sur le binaire de l’exécutable natif.
  • &#13 ;

  • Les tests d’écriture à Quarkus ne sont pas particulièrement difficiles. Le cadre fournit suffisamment d’outils pour créer des talons et créer facilement des maquettes (via Mockito, PanacheMock), ainsi que pour rendre vos tests non dépendants de l’environnement en utilisant l’annotation @TestHTTPEndpoint.
  • &#13 ;

Qu’est-ce que Quarkus ? Quarkus est un framework Java complet, natif de Kubernetes, conçu pour les machines virtuelles Java (JVM) et la compilation native.

Quarkus optimise Java spécifiquement pour les conteneurs et lui permet de devenir une plate-forme efficace pour les environnements sans serveur, dans le nuage et Kubernetes. Au lieu de réinventer la roue, Quarkus utilise des cadres de travail bien connus des entreprises, soutenus par des normes/spécifications, et les rend compilables en binaire à l’aide de Graal VM.

Dans cet article, nous apprendrons comment écrire des tests de composants/intégration pour les applications Quarkus.

Nous n’allons pas voir de tests unitaires pour une raison simple : les tests unitaires sont écrits sans avoir l’environnement d’exécution dans le contexte du test, donc généralement, ils sont juste écrits avec quelques bibliothèques – à mon avis, Junit5 + Mockito + AssertJ est la meilleure combinaison. Ainsi, tout ce que vous avez utilisé pour vos tests unitaires existants est valable dans une application Quarkus.

Mais qu’en est-il des tests de composants/intégration où l’environnement d’exécution entre en scène ? Apprenons à les écrire dans Quarkus.

Considérons un petit cas d’utilisation où nous construisons une API REST pour enregistrer les utilisateurs.

Tout utilisateur qui souhaite s’inscrire à notre système doit nous fournir un nom d’utilisateur et un courriel. Un mot de passe automatique est ensuite généré et l’utilisateur est enregistré dans la base de données.

Si l’on parle de la conception de l’application, elle est composée des classes suivantes :

Nous n’allons pas inspecter en profondeur le code de l’application, mais seulement les parties nécessaires aux tests.

RegistrationResource

Tout d’abord, vous pouvez voir le point final de l’API REST qui met en œuvre les opérations d’enregistrement :

@Path("/registration")
public class RegistrationResource {

   @Inject
   PasswordGenerator passwordGenerator;

   @GET
   @Path("/{username}")
   @Produces(MediaType.APPLICATION_JSON)
   public Response findUserByUsername(@PathParam("username") String username) {
       return User.findUserByUsername(username)
                   .map(u -> Response.ok(u).build())
                   .orElseGet(() -> Response.status(Status.NOT_FOUND).build());
   }

   @POST
   @Transactional
   @Consumes(MediaType.APPLICATION_JSON)
   public Response insertUser(User user) {
      
       user.password = passwordGenerator.generate();
       user.persist();

       URI userUri = UriBuilder.fromResource(RegistrationResource.class)
                                         .path("https://www.infoq.com/" + user.id).build();
       return Response
                   .created(userUri)
                   .build();
   }
}

Les choses importantes dans cette classe :

    &#13 ;
  • Il y a 2 terminaux, un pour insérer un utilisateur et un pour obtenir des informations à son sujet.
  • &#13 ;

  • Il délègue la génération des mots de passe à une mise en œuvre de la PasswordGenerator classe. Il est injecté en utilisant le CDI (Context and Dependency Injection).
  • &#13 ;

  • User est une implémentation du modèle d’enregistrement actif. Plus d’informations à ce sujet plus tard.
  • &#13 ;

Utilisateur

L’entité utilisateur utilise la spécification de l’API de persistance de Java (JPA) et met en œuvre le modèle d’enregistrement actif. Pour cette raison, elle étend le PanacheEntity classe.

@Entity
public class User extends PanacheEntity {
   @Column public String username;
   @Column public String email;
   @Column public String password;

   public static Optional<User> findUserByUsername(String username) {
       return find("username", username).firstResultOptional();
   }
}

PasswordGenerator

La dernière classe importante est le générateur de mots de passe qui crée un mot de passe aléatoire pour l’utilisateur.

@ApplicationScoped
public class RandomPasswordGenerator implements PasswordGenerator {

   @Override
   public String generate() {
       return UUID.randomUUID().toString();
   }
  
}

Il est important de noter ici que le RandomPasswordGenerator est un haricot géré par le conteneur CDI en raison ApplicationScoped annotation.

Nous allons commencer par une vérification complète de la logique, puis nous verrons étape par étape comment réduire la portée du test en utilisant certaines des installations de test de Quarkus.

Commençons à écrire un test de composants qui vérifie que toute la logique est correctement exécutée. Je sais que certains d’entre vous se demandent peut-être “n’est-ce pas un test de bout en bout ?” Et c’est une question juste, mais attendez une seconde et vous comprendrez pourquoi j’appelle cela un test de composant.

Test des composants. Test de toute la logique.

Nous voulons écrire des tests pour la classe API REST afin de vérifier si chaque terminal REST donne le bon HTTP ResponseCode ou non, s’il renvoie le JSON attendu ou non, etc.

La première chose que nous devons réparer est le serveur de base de données. Si nous voulons valider que l’ensemble du flux fonctionne comme prévu, nous avons besoin d’une base de données pour que les utilisateurs puissent être stockés et les retrouver plus tard. Nous pourrions utiliser la même base de données qu’en production, mais cela ralentirait l’exécution des tests car la base de données serait déployée en dehors du test, ce qui signifie que les tests devraient attendre que la base de données soit opérationnelle et ensuite exécuter toutes les opérations de la base de données vers une instance distante.

Comme nous voulons une exécution rapide des tests et que nous ne voulons pas écrire un test de bout en bout ou un test d’intégration, nous utilisons une base de données intégrée en mémoire comme H2.

Pouvons-nous mettre en place une configuration concrète de la source de données juste pour l’exécution du test ? La réponse est oui, avec les profils Quarkus.

Quarkus vous permet d’avoir plusieurs valeurs de configuration pour la même propriété en la préfixant par un profil.

Nous pouvons utiliser trois profils par défaut :

    &#13 ;
  • dev: Activé en mode développement (quarkus:dev)
  • &#13 ;

  • test: Activé lors de l’exécution de tests
  • &#13 ;

  • prod: Le profil par défaut lorsqu’il ne fonctionne pas en mode développement ou test
  • &#13 ;

Pour définir les valeurs de configuration pour chaque profil, nous devons ouvrir le fichier de configuration situé à src/main/resources/application.properties et utiliser la syntaxe suivante : .config.key=value.

Un exemple de configuration de deux connexions JDBC, l’une pour les tests et l’autre pour la production, est présenté dans l’extrait suivant :

quarkus.datasource.jdbc.url=jdbc:mariadb://localhost:3306/mydb
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1

Ouvrons le fichier application.properties et configurons une source de données à utiliser pendant l’exécution du test :

%test.quarkus.datasource.db-kind=h2
%test.quarkus.datasource.username=sa
%test.quarkus.datasource.password=
%test.quarkus.datasource.jdbc.url=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
%test.quarkus.hibernate-orm.database.generation=update

Dans ce cas, H2 est utilisé comme une base de données en mémoire intégrée pendant l’exécution du test.

Quarkus s’appuie sur JUnit 5 comme cadre de test et sur REST-Assured pour tester et valider les services REST.

L’annotation la plus importante dans un test Quarkus est l’annotation @QuarkusTest. Cette annotation est utilisée pour démarrer l’application Quarkus sur le port 8081. Même si vous disposez de plusieurs classes de test (toutes annotées par @QuarkusTest), le processus de démarrage de l’application n’est exécuté qu’une seule fois par défaut pour l’ensemble de la suite de tests.

Écrivons une classe de test pour notre test de composant de la boîte blanche. Nous allons maintenant vérifier qu’un utilisateur est bien enregistré.


import io.quarkus.test.junit.QuarkusTest;

import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response.Status;


@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RegistrationResourceTest {

   @Test
   @Order(1)
   public void shouldRegisterAUser() {
       final User user = new User();
       user.username = "Alex";
       user.email = "asotobu@example.com";

       given()
         .body(user)
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
         .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
         .when()
         .post("/registration")
         .then()
            .statusCode(Status.CREATED.getStatusCode())
            .header("location", "http://localhost:8081/registration/1");
   }
}
    &#13 ;
  • Le test est annoté avec @QuarkusTest.
  • &#13 ;

  • @TestMethodOrder est une annotation de l’unité 5 pour fixer l’ordre d’exécution de chaque cas test.
  • &#13 ;

  • @Order précise l’ordre dans lequel le test est effectué.
  • &#13 ;

  • REST-Assured commence par le given() méthode statique.
  • &#13 ;

  • Le body() est utilisée pour définir le corps du message de la demande.
  • &#13 ;

  • L’URL de base est définie automatiquement (http://localhost:8081) et il suffit de définir la méthode HTTP et le chemin d’accès en utilisant la méthode post (“/registration”).
  • &#13 ;

  • Il vérifie que le code de statut est 201 et la valeur de l’en-tête de localisation est la bonne.
  • &#13 ;

Nous pouvons faire quelque chose de similaire pour tester qu’un utilisateur peut être récupéré de l’application :

@Test
@Order(2)
public void shouldFindAUserByUsername() {
    given()
      .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
      .when()
      .get("/registration/{username}", "Alex")
      .then()
         .statusCode(200)
         .body("username", is("Alex"))
         .body("email", is("asotobu@example.com"))
         .body("password", notNullValue());
    &#13 ;
  • La méthode HTTP est maintenant un get, nous utilisons donc la get() méthode.
  • &#13 ;

  • Après la méthode then() viennent les assertions. La méthode body() est utilisée pour valider la réponse du contenu du corps. Le premier paramètre est une expression de chemin JSON pour spécifier le champ JSON que nous voulons vérifier et le second paramètre est la valeur attendue.
  • &#13 ;

Dans les deux tests, vous remarquerez peut-être que bien que l’URL de base soit automatiquement définie dans REST-Assured, le chemin d’accès ne l’est pas et nous devons le faire manuellement (c’est-à-dire .post("/registration")). Quarkus utilise @io.quarkus.test.common.http.TestHTTPEndpoint pour injecter la configuration du chemin dans REST-Assured. Réécrivons donc le test afin de ne pas avoir à définir le sous-chemin dans chaque requête.

@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestHTTPEndpoint(RegistrationResource.class)
public class RegistrationResourceTest {

   @Test
   @Order(1)
   public void shouldRegisterAUser() {
       final User user = new User();
       user.username = "Alex";
       user.email = "asotobu@example.com";

       given()
         .body(user)
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
         .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
         .when().post()
         .then()
            .statusCode(Status.CREATED.getStatusCode())
            .header("location", "http://localhost:8081/registration/1");
    &#13 ;
  • Dans le TestHTTPEndpoint annotation, vous mettez la ressource à l’épreuve.
  • &#13 ;

  • Le post() est vide, car le chemin est automatiquement récupéré dans la ressource.
  • &#13 ;

Le test est maintenant plus résistant aux changements de valeur de la voie car il est automatiquement configuré avec REST-Assured.

Mais il y a quand même une valeur codée en dur sur l’affirmation (.header("location", "http://localhost:8081/registration/1")), donc si le test est exécuté sur un autre port ou si le chemin est modifié, le test échouerait à cause du refactoriel. Quarkus utilise le @io.quarkus.test.common.http.TestHTTPResource pour injecter l’emplacement du point final dans le test.

@TestHTTPResource
@TestHTTPEndpoint(RegistrationResource.class)
URL url;

@Test
@Order(1)
public void shouldRegisterAUser() {

    final User user = new User();
    user.username = "Alex";
    user.email = "asotobu@example.com";

    given()
      .body(user)
      .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
      .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
      .when().post()
      .then()
         .statusCode(Status.CREATED.getStatusCode())
         .header("location", url + "/1");
    &#13 ;
  • @TestHTTPResource et @TestHTTPEndpoint(RegistrationResource.class) sont utilisés ensemble pour injecter l’URL de base et le chemin d’accès.
  • &#13 ;

La bastonnade et la moquerie

Jusqu’à présent, nous avons testé l’ensemble de l’application, mais jetez un coup d’œil à l’une des affirmations de la shouldFindAUserByUsernameet plus précisément celui-ci : .body("password", notNullValue(). Remarquez que nous n’affirmons pas la valeur (car elle est générée de manière aléatoire), nous vérifions seulement que le champ est bien défini. Mais que se passe-t-il si nous voulons vérifier non seulement que le mot de passe généré est défini, mais aussi que la valeur est correcte ? Ou que se passe-t-il si la logique prend une seconde pour générer un mot de passe et que nous voulons éviter de faire cet appel coûteux dans nos tests ? Nous avons alors deux possibilités : soit nous trompons la logique, soit nous nous moquons de la logique. Expliquer la différence entre les deux approches n’entre pas dans le cadre de cet article, mais vous pouvez penser à un talon comme un objet qui contient des données prédéfinies tandis que les moqueries sont des objets avec des réponses préenregistrées qui enregistrent les appels qu’ils reçoivent pour une validation future.

Voyons comment utiliser les talons et les moqueries dans un test de Quarkus.

Stubbing

Pour créer un talon de RandomPasswordGeneratornous devons créer une mise en œuvre de PasswordGenerator à l’interface src/test/java l’emplacement. En outre, cette classe doit être annotée avec @io.quarkus.test.Mock.

@Mock
public class StaticPasswordGenerator implements PasswordGenerator {

   @Override
   public String generate() {
       return "my-secret-password";
   }
}

Ensuite, nous mettons à jour le test avec le mot de passe concret défini dans la classe de talon :

@Test
@Order(2)
public void shouldFindAUserByUsername() throws InterruptedException {
    given()
      .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
      .when().get("/{username}", "Alex")
      .then()
         .statusCode(200)
         .body("username", is("Alex"))
         .body("email", is("asotobu@example.com"))
         .body("password", is("my-secret-password"));

Remarquez que maintenant, nous connaissons le mot de passe à l’avance, ce qui nous permet d’affirmer une valeur concrète rendue par la classe de poilus.

Maintenant, lorsque nous effectuons le test, au lieu d’injecter le RandomPasswordGenerator l’instance, le StaticPasswordGenerator est injectée dans la classe de ressources.

L’inconvénient de cette approche est que la même instance de talon est partagée dans toute l’exécution de la suite de tests, ce qui peut être valable dans certains cas mais pas dans d’autres.

Quarkus soutient également l’utilisation de la célèbre bibliothèque Mockito pour écrire des moqueries.

Moquerie

Pour utiliser Mockito dans Quarkus, nous devons ajouter le quarkus-junit5-mockito dépendance dans le script de notre outil de construction. Par exemple, dans Maven, vous devez ajouter la section suivante dans pom.xml:

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-junit5-mockito</artifactId>
   <scope>test</scope>
</dependency>

Pour créer une maquette pour notre test, nous devons utiliser le @io.quarkus.test.junit.mockito.InjectMock pour injecter automatiquement la maquette dans le conteneur CDI.

@InjectMock
PasswordGenerator passwordGenerator;

@Test
@Order(1)
public void shouldRegisterAUser() {
      
    Mockito.when(passwordGenerator.generate()).thenReturn("my-secret-password");

    final User user = new User();
    user.username = "Alex";
    user.email = "asotobu@example.com";

    given()
      .body(user)
      .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
      .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
      .when().post()
      .then()
         .statusCode(Status.CREATED.getStatusCode())
         .header("location", url + "/1");
    &#13 ;
  • @InjectMock injecte un faux dans le test. Si vous connaissez le cadre Mockito, c’est une sorte de org.mockito.Mock annotation.
  • &#13 ;

  • La simulation ne prend effet que pendant la durée de la méthode d’essai. Si vous l’utilisez dans le cadre de la @BeforeAll puis il est enregistré pour toute la classe de test.
  • &#13 ;

  • La maquette créée est automatiquement injectée dans le conteneur CDI et c’est l’instance injectée dans le RegistrationResource exemple.
  • &#13 ;

Quarkus offre une expérience agréable lorsqu’il s’agit d’écrire un test en boîte noire, mais qu’en est-il des tests en boîte blanche, lorsque nous nous soucions de la conception interne des classes ou par exemple lorsque nous ne voulons pas tester l’application en faisant un appel REST mais un appel de méthode ? Quarkus vous aide également dans ce domaine.

Test des composants. Test de la boîte blanche.

Jusqu’à présent, nous avons écrit des tests qui vérifient l’exactitude de l’application en faisant des appels à l’API REST, mais comme nous le savons déjà, parfois nous ne voulons pas tester l’application de l’extérieur (boîte noire) mais de l’intérieur (boîte blanche). Par exemple, dans cette catégorie se trouvent certains types de tests tels que les tests de persistance, les tests d’intégration ou certains tests de composants.

La bonne nouvelle, c’est que tout test effectué à Quarkus est un test CDI, ce qui signifie que tout ce qui est valable en CDI l’est également en test, vous pouvez utiliser @Inject pour injecter tout haricot d’affaires, vous pouvez les faire @Transactionalou même créer des méta-annotations.

Testons le RandomPasswordGenerator mais au lieu de l’instancier directement dans le test, nous allons utiliser l’instance qui est créée par le conteneur CDI, donc proche de ce qui est utilisé en production.


@QuarkusTest
public class PasswordGeneratorTest {
  
   @Inject
   PasswordGenerator passwordGenerator;

   @Test
   public void shouldGenerateARandomPassword() {
       final String password = passwordGenerator.generate();
       assertThat(password).containsPattern("[0-9A-F-]+");
   }
}
    &#13 ;
  • Comme le test est un haricot CDI, le RandomPasswordGenerator est injectée.
  • &#13 ;

  • Aucune API REST ne se produit, car nous utilisons AssertJ comme bibliothèque d’assertions.
  • &#13 ;

Tests de persistance

Les tests de persistance sont un autre type de test qu’il est utile d’écrire, surtout lorsqu’il n’y a pas de questions standard. Comme tout test Quarkus est un haricot CDI, il est très facile d’écrire des tests de persistance car tout test peut être transactionnel et un gestionnaire d’entité est instancié.

Écrivons un test de persistance qui valide simplement que trouver un utilisateur par son nom d’utilisateur fonctionne.

@QuarkusTest
public class UserTest {
  
   @Test
   @Transactional
   public void shouldFindUsersByUsername() {

       final User user = new User();
       user.username = "Alex";
       user.email = "asotobu@example.com";
       user.persist();

       Optional<User> foundUser = User.findUserByUsername("Alex");

       assertThat(foundUser)
           .isNotEmpty()
           .map(u -> u.email)
           .contains("asotobu@example.com");

   }
}
    &#13 ;
  • Le test est transactionnel pour maintenir l’insert.
  • &#13 ;

  • EntityManager est injectée automatiquement dans l’entité de l’utilisateur.
  • &#13 ;

TIP: @Transactional L’annotation sur les tests signifie que les modifications que votre test apporte à la base de données sont persistantes, ce qui peut avoir un effet secondaire dans un autre test. Si vous souhaitez que les modifications apportées soient annulées à la fin du test, vous pouvez annoter le test avec @io.quarkus.test.TestTransaction.

Mais que se passe-t-il si nous devons tester une logique commerciale qui contient un code de persistance, et que nous ne voulons pas nous fier à la base de données ? Dans un cas normal, nous pourrions créer une maquette, mais avec l’implémentation du Panache Active Record Pattern, les opérations sont définies dans l’entité et certaines d’entre elles sont des méthodes statiques (c’est-à-dire des finders), alors comment pouvons-nous les simuler ?

Tests de persistance moqueurs

Supposons que nous ayons développé une classe pour régénérer le mot de passe au cas où le mot de passe original serait perdu.

Cette classe doit trouver le User puis mettre à jour le mot de passe à l’aide du générateur de mots de passe aléatoires.

@ApplicationScoped
public class RegeneratePassword {
  
   @Inject
   PasswordGenerator passwordGenerator;

   @Transactional
   public void regenerate(String username) {

       final Optional<User> user = User.findUserByUsername(username);
       user.map( u -> {
           String newPassword = passwordGenerator.generate();
           u.password = newPassword;
           return u;
       });

   }
}

Pour utiliser Mockito et Panache dans Quarkus, nous devons ajouter le quarkus-panache-mock

dépendance à l’égard de notre script d’outil de construction. Par exemple, dans Maven, vous devez ajouter la section suivante dans pom.xml:

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-panache-mock</artifactId>
  <scope>test</scope>
</dependency>

Pour se moquer RegeneratePassword pour notre test, nous devons utiliser le @io.quarkus.test.junit.mockito.InjectMock annotation pour PasswordGenerator comme dans la section précédente, et utilisez également le io.quarkus.panache.mock.PanacheMock pour créer une simulation de PanacheEntity. Dans notre cas d’utilisation actuel, nous utilisons un PanacheMock.mock(User.class) appel.

@QuarkusTest
public class RegeneratePasswordTest {
  
   @InjectMock
   PasswordGenerator passwordGenerator;

   @Inject
   RegeneratePassword regeneratePassword;

   @Test
   public void shouldGenerateANewPassword() {
      
       Mockito.when(passwordGenerator.generate()).thenReturn("my-secret").thenReturn("password");
      
       PanacheMock.mock(User.class);
       User user = new User("Alex", "alex@example.com", "my_super_password");
       Mockito.when(User.findUserByUsername("Alex")).thenReturn(Optional.of(user));

       regeneratePassword.regenerate("Alex");

       PanacheMock.verify(User.class, Mockito.times(1)).findUserByUsername("Alex");
       assertThat(user.password).isEqualTo("my-secret");

   }
}
    &#13 ;
  • PanacheMock est utilisé pour se moquer de l’entité haricot.
  • &#13 ;

  • Les méthodes normales de Mockito sont utilisées comme nous le faisons normalement avec les méthodes statiques.
  • &#13 ;

  • Pour vérifier toute interaction, PanacheMock.verify doit être utilisée.
  • &#13 ;

Test Native Executable

L’une des caractéristiques intéressantes de Quarkus est la possibilité de compiler l’application en un exécutable natif à l’aide de GraalVM.

Bien que ce processus devrait fonctionner en permanence, il est également vrai que lorsqu’un exécutable natif est exécuté, quelques problèmes peuvent survenir. C’est pourquoi il est judicieux d’effectuer quelques tests sur le fichier exécutable natif.

Quarkus fournit le io.quarkus.test.junit.NativeImageTest annotation pour que les tests démarrent le fichier exécutable natif au lieu de démarrer l’application dans la machine virtuelle Java.

Ces types de tests sont généralement effectués comme integration testsPar conséquent, lorsqu’un projet Quarkus est échafaudé, le plugin Maven Failsafe Plugin est utilisé contrairement au plugin Maven Surefire. De cette façon, les tests sont exécutés dans la phase de test d’intégration de Maven au lieu de la phase de test d’intégration de test La phase et les tests doivent aboutir à IT au lieu de Test.

IMPORTANT : Vous devez avoir GraalVM installé sur votre machine afin de compiler vers un exécutable natif.

Disposer d’outils GraalVM et d’images natives (${GRAALVM_HOME}/bin/gu install native-image) installé, nous devons définir la variable d’environnement GRAALVM_HOME dans le répertoire d’installation de GraalVM.

export GRAALVM_HOME=/Users/asotobu/Applications/graalvm-ce-java11-20.2.0/Contents/Home

Faisons un test de la boîte blanche qui vérifie que le fichier natif fonctionne correctement.

@NativeImageTest
public class NativeRegistrationResourceIT {

   @Test
   public void shouldRegisterAUser() {
      
       final User user = new User();
       user.username = "Alex";
       user.email = "asotobu@example.com";

       given()
         .body(user)
         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
         .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
         .when().post()
         .then().statusCode(Status.CREATED.getStatusCode());
   }
}
    &#13 ;
  • NativeImageTest L’annotation est utilisée à la place de QuarkusTest annotation.
  • &#13 ;

Enfin, nous pouvons créer un exécutable natif et lancer le test en exécutant la commande suivante :

./mvnw integration-test -Pnative

Conclusions

Faire des tests dans Quarkus n’est pas si difficile, et il vous donne suffisamment d’outils pour créer des talons et créer facilement des simulations (Mockito, PanacheMock) ainsi que pour rendre vos tests non dépendants de l’environnement en utilisant l’annotation @TestHTTPEndpoint.

Mais il y a bien d’autres choses que Quarkus vous propose pour vous aider à tester une application et que nous allons couvrir dans la deuxième partie de cet article.

Code source.

À propos de l’auteur

Alex Soto est directeur de l’expérience des développeurs chez Red Hat. Il est passionné par le monde Java, l’automatisation des logiciels et il croit au modèle des logiciels libres. Alex est le co-auteur de Testing Java Microservices et de livres de recettes Quarkus et contribue à plusieurs projets de logiciels libres. Champion Java depuis 2017, il est également conférencier international et enseignant à l’université Salle URL. Vous pouvez le suivre sur Twitter (@alexsotob) pour rester au courant de ce qui se passe dans le monde de Kubernetes et Java.