Maven est l'outil de construction le plus populaire dans l'espace Java, tandis que les tests d'intégration constituent une partie essentielle du processus de développement. Par conséquent,it’s a natural choice to configure and execute integration tests with Maven.
Dans ce didacticiel, nous allons passer en revue différentes manières d'utiliser Maven pour les tests d'intégration et de séparer les tests d'intégration des tests unitaires.
2. Préparation
Pour rendre le code de démonstration proche d'un projet réel, nous allons configurer une application JAX-RS. Cette application est déployée sur un serveur avant l'exécution des tests d'intégration puis démantelée.
2.1. Configuration Maven
Nous allons créer notre application REST autour de Jersey, l’implémentation de référence de JAX-RS. Cette implémentation nécessite quelques dépendances:
Nous pouvons trouver les dernières versions de ces dépendanceshere ethere.
Nous utiliserons le plug-in Jetty Maven pour configurer un environnement de test. This plugin starts a Jetty server during the pre-integration-test phase of the Maven build lifecycle, then stops it in the post-integration-test phase.
Voici comment nous configurons le plugin Jetty Maven danspom.xml:
Lorsque le serveur Jetty démarre, il écoute sur le port8999. Les éléments de configurationstopKey etstopPort sont utilisés uniquement par l'objectifstop du plugin et leur valeur n'est pas importante de notre point de vue.
Here est l'endroit où trouver la dernière version du plugin Jetty Maven.
Une autre chose à noter est que nous devons définir l'élémentpackaging du fichierpom.xml surwar, sinon le plugin Jetty ne peut pas démarrer le serveur:
war
2.2. Créer une application REST
Le point final de l'application est très simple: il renvoie un message de bienvenue lorsqu'une demande GET frappe la racine de contexte:
@Path("/")
public class RestEndpoint {
@GET
public String hello() {
return "Welcome to example!";
}
}
Voici comment enregistrer la classe de points de terminaison avec Jersey:
package com.example.maven.it;
import org.glassfish.jersey.server.ResourceConfig;
public class EndpointConfig extends ResourceConfig {
public EndpointConfig() {
register(RestEndpoint.class);
}
}
Pour que le serveur Jetty connaisse notre application REST, nous pouvons utiliser un descripteur de déploiement classique deweb.xml:
This descriptor must be placed in the directory /src/main/webapp/WEB-INF à reconnaître par le serveur.
2.3. Code de test côté client
Toutes les classes de test dans les sections suivantes contiennent une seule méthode:
@Test
public void whenSendingGet_thenMessageIsReturned() throws IOException {
String url = "http://localhost:8999";
URLConnection connection = new URL(url).openConnection();
try (InputStream response = connection.getInputStream();
Scanner scanner = new Scanner(response)) {
String responseBody = scanner.nextLine();
assertEquals("Welcome to example!", responseBody);
}
}
Comme nous pouvons le constater, cette méthode ne fait qu’envoyer une requête GET à l’application Web que nous avons précédemment configurée et en vérifiant la réponse.
3. Test d'intégration en action
Une chose importante à noter à propos des tests d'intégration est quetest methods often take quite a long time to run.
Par conséquent, nous devrions exclure les tests d'intégration du cycle de vie de génération par défaut, en évitant qu'ils ne ralentissent l'ensemble du processus à chaque fois que nous construisons un projet.
A convenient way to separate integration tests is to use build profiles. Ce type de configuration nous permet d'exécuter des tests d'intégration uniquement lorsque cela est nécessaire - en spécifiant un profil approprié.
Dans les sections suivantes, nous allons configurer tous les tests d'intégration avec des profils de build.
4. Test avec le plug-in Failsafe
La manière la plus simple d'exécuter des tests d'intégration est d'utiliserthe Maven failsafe plugin.
Par défaut, le plugin Mavensurefire exécute des tests unitaires pendant la phasetest, tandis quethe failsafe plugin runs integration tests in the integration-test phase.
Nous pouvons nommer des classes de tests avec des modèles différents pour que ces plugins récupèrent les tests inclus séparément.
Les conventions de dénomination par défaut appliquées parsurefire etfailsafe sont différentes, il suffit donc de suivre ces conventions pour séparer les tests unitaires et d'intégration.
L'exécution du pluginsurefire inclut toutes les classes dont le nom commence parTest, ou se termine parTest,Tests ouTestCase. En revanche, le pluginfailsafe exécute des méthodes de test dans les classes dont le nom commence parIT, ou se termine parIT ouITCase.
This est l'endroit où nous pouvons trouver la documentation concernant l'inclusion de test poursurefire, ethere est celle pourfailsafe.
Ajoutons le pluginfailsafe au POM avec la configuration par défaut:
This link est l'endroit où trouver la dernière version du pluginfailsafe.
Avec la configuration ci-dessus, la méthode de test suivante sera exécutée dans la phaseintegration-test:
public class RestIT {
// test method shown in subsection 2.3
}
Puisque le serveur Jetty démarre dans la phasepre-integration-test et s'arrête danspost-integration-test, le test que nous venons de voir passe avec cette commande:
mvn verify -Pfailsafe
Nous pouvons également personnaliser les modèles de dénomination pour inclure des classes avec des noms différents:
En dehors du pluginfailsafe,we can also use the surefire plugin to execute unit and integration tests in different phases.
Supposons que nous voulions nommer tous les tests d'intégration avec le suffixeIntegrationTest. Puisque le pluginsurefire exécute par défaut des tests avec un tel nom dans la phasetest, nous devons les exclure de l'exécution par défaut:
Nous avons retiré toutes les classes de test dont le nom se termine parIntegrationTest du cycle de vie de la compilation. Il est temps de les remettre avec un profil:
Instead of binding the test goal of the surefire plugin to the test build phase, as usual, we bound it to the integration-test phase. Le plugin se lancera alors pendant le processus de test d'intégration.
Notez que nous devons définir un élémentexclude surnone pour remplacer l'exclusion spécifiée dans la configuration de base.
Maintenant, définissons une classe de test d'intégration avec notre modèle de dénomination:
public class RestIntegrationTest {
// test method shown in subsection 2.3
}
Ce test sera exécuté avec la commande:
mvn verify -Psurefire
6. Tester avec le plug-in Cargo
Nous pouvons utiliser le pluginsurefire avec le plugin Mavencargo. Ce plugin est livré avec un support intégré pour les serveurs intégrés, qui sont très utiles pour les tests d'intégration.
Plus de détails sur cette combinaison peuvent être trouvéshere.
7. Test avec les@Category de JUnit
Un moyen pratique d'exécuter des tests de manière sélective consiste à tirer parti dethe @Category annotation dans le framework JUnit 4. This annotation lets us exclude particular tests from unit testing, and include them in integration testing.
Tout d'abord, nous avons besoin d'une interface ou d'une classe pour fonctionner en tant qu'identificateur de catégorie:
package com.example.maven.it;
public interface Integration { }
On peut ensuite décorer une classe de test avec l'annotation@Category et l'identifiantIntegration:
@Category(Integration.class)
public class RestJUnitTest {
// test method shown in subsection 2.3
}
Plutôt que de déclarer l'annotation@Category sur une classe de test, nous pouvons également l'utiliser au niveau de la méthode pour catégoriser les méthodes de test individuelles.
Exclure une catégorie de la phase de construction detest est simple:
Nous pouvons maintenant exécuter des tests d'intégration avec une commande Maven:
mvn verify -Pcategory
8. Ajout d'un répertoire distinct pour les tests d'intégration
Il est parfois souhaitable d’avoir un répertoire distinct pour les tests d’intégration. Organizing tests this way allows us to entirely isolate integration tests from unit tests.
Nous pouvons utiliser le plugin Mavenbuild helper à cet effet:
Here est l'endroit où nous pouvons trouver la dernière version de ce plugin.
La configuration que nous venons de voir ajoute un répertoire source de test à la construction. Ajoutons une définition de classe à ce nouveau répertoire:
public class RestITCase {
// test method shown in subsection 2.3
}
Il est temps d’exécuter des tests d’intégration dans cette classe:
mvn verify -Pfailsafe
Le plugin Mavenfailsafe exécutera les méthodes de cette classe de test en raison de la configuration définie dans la sous-section 3.1.
Un répertoire de source de test est souvent associé à un répertoire de ressources. Nous pouvons ajouter un tel répertoire dans un autre élémentexecution de la configuration du plugin:
Cet article est passé en revue l'utilisation de Maven pour exécuter des tests d'intégration avec un serveur Jetty, en se concentrant sur la configuration des plugins Mavensurefire etfailsafe.
Le code source complet de ce didacticiel se trouve àover on GitHub.