Publié par
Il y a 3 années · 4 minutes · Craft

Séparer les tests d’intégration avec Maven

Lors de nos développements, il est bien pratique de pouvoir lancer les tests unitaires sans lancer les (longs) tests d’intégration.

Dans un projet Maven, les tests unitaires et d’intégration sont traditionnellement placés sous le répertoire src/test/java :

src/main/java/
 ╰─ com
  ╰─ xebia
   ╰─ project
    ╰─ MyComponent.java

src/test/java/
 ╰─ com
  ╰─ xebia
   ╰─ project
    ╰─ MyComponentTest.java
    ╰─ MyComponentITest.java 

C’est la solution la plus simple. L’avantage étant que les tests (unitaires et d’intégration) peuvent ainsi respecter l’arborescence des packages du main.

Cependant, du point de vue arborescence de fichiers, il n’y a pas de séparation nette entre tests unitaires et tests d’intégration.

De plus, il devient difficile de lancer les tests unitaires (par exemple avant un commit) sans lancer les tests d’intégration. S’il est vrai que Maven fournit la possibilité d’exclure des tests en se basant sur une expression (par exemple tous les *ITest.java) via le plugin failsafe lors d’un mvn test, c’est toutefois plus difficile (voire impossible) à obtenir en utilisant les outils intégrés aux IDE (comme Eclipse) quand on veut lancer tous les tests unitaires ensemble (« Run as Junit test » sur un dossier). Or, pour les développeurs il est nécessaire de pouvoir passer les tests unitaires avant chaque commit, sans avoir à passer également les tests d’intégrations qui peuvent prendre de plusieurs minutes à plusieurs heures pour s’exécuter. Si certains IDE (comme IntelliJ) permettent d’exclure des tests en se basant sur une expression rationnelle, cela oblige à figer le nom desdits tests, par exemple en *Itest.java.

Quelles solutions avons-nous à notre disposition pour résoudre ce problème ?

 

Placer les tests d’intégration dans un paquetage séparé

Il est possible, pour séparer nettement les tests unitaires et tests d’intégration, de placer ces derniers dans un package dédié :

src/main/java/
    ╰─ com
        ╰─ xebia
            ╰─ project
                ╰─ MyComponent.java
src/test/java/
    ╰─ com
        ╰─ xebia
            ╰─ project
                ╰─ MyComponentTest.java
    ╰─ it
        ╰─ com
            ╰─ xebia
                ╰─ project
                    ╰─ MyComponentITest.java

Avec cette solution, on obtient bien la séparation nette désirée entre classes de tests unitaires et classes de tests d’intégration. Toutefois, pour les tests d’intégration, on perd alors la possibilité de respecter l’arborescence des paquetages du main, avec ce que cela entraine (pas accès aux membres package-private et protected).

Placer les tests d’intégration dans un module séparé

C’est une solution qui a du sens dans le cas de tests d’intégration haut niveau (Selenium). Cependant l’expérience m’a prouvé qu’une fois que les tests d’intégration sont placés dans un module séparé du projet principal, personne ne télécharge ce module. Les tests d’intégration ne sont alors que peu ou plus maintenus (ne parlons pas d’en ajouter de nouveaux). En effet, cela fait deux fois plus de projets à gérer, ce qui est particulièrement pénible, par exemple, dans une architecture SOA où les développeurs travaillent déjà avec une myriade de projets différents dans leur espace de travail.

Placer les tests d’intégration dans un dossier réservé

Une solution idéale serait d’avoir un dossier spécifique pour les tests d’intégration, comme par exemple src/it/java :

src/main/java/
 ╰─ com
  ╰─ xebia
   ╰─ project
    ╰─ MyComponent.java

src/test/java/
 ╰─ com
  ╰─ xebia
   ╰─ project
    ╰─ MyComponentTest.java

src/it/java/
 ╰─ com
  ╰─ xebia
   ╰─ project
     ╰─ MyComponentITest.java 

Cependant une telle arborescence ne respecte plus les conventions d’organisation de projet Maven, ce qui signifie que les tests placés dans src/it/java ne seront pas lancés lors de la phase test de la construction du projet. Mais il est possible de spécifier à Maven un (ou plusieurs) répertoires additionnels où chercher les tests. Ceci s’effectue grâce au plugin build-helper. Pour ce faire, il faut ajouter dans la section build du pom.xml le code suivant :

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>build-helper-maven-plugin</artifactId>
    <executions>
        <execution>
            <id>add-test-source</id>
            <phase>process-resources</phase>
            <goals>
                <goal>add-test-source</goal>
            </goals>
            <configuration>
                <sources>
                    <source>src/it/java</source>
                </sources>
            </configuration>
        </execution>
    </executions>
</plugin>

Lorsque l’on observe l’intégration dans l’IDE Eclipse (ou d’autres), on constate qu’à présent, le répertoire src/it/java est bien considéré par Maven comme un répertoire de sources, au même niveau que src/main/java et src/test/java :

Cette approche comble les faiblesses des solutions des sections précédentes procurant des avantages certains pour les tests d’intégration qui sont à présent :

  • clairement séparés des tests unitaires, ce qui permet aux développeurs de lancer les tests unitaires facilement avec les outils de l’IDE (en dehors d’un mvn test) ;
  • qui ne sont pas forcés de porter un nom particulier (ex. *ITest.java) pour les distinguer des tests unitaires ;
  • avec une structure de test qui s’intègre dans le processus de build Maven ;
  • respectueux de la structure de packages du main ;
  • inclus dans le même module que le projet concerné.

 

Bastien Bonnet
Bastien est un développeur disposant de 4 ans d'expérience. Il est passionné par le développement de logiciel de qualité (code clair, facile à maintenir, robuste face aux régression (tests automatisés)). Agiliste convaincu, il s'inscrit parfaitement dans le mouvement du software craftsmanship. Il est convaincu et investi dans le partage de connaissance pour améliorer le niveau technique et les compétences de son équipe.

7 réflexions au sujet de « Séparer les tests d’intégration avec Maven »

  1. Publié par JY, Il y a 3 années

    Une info bien utile qui deviendra j’espère la convention ! :)
    Je n’ai jamais compris pourquoi Maven n’avait pas déjà « normaliser » la mise en oeuvre des tests d’intégrations … Dans Maven4 peut-être ;)

  2. Publié par nrichand, Il y a 3 années

    Amusant, un blog bien connu avait fait un article similaire exactement 4 ans jour pour jour ( http://bit.ly/1hQP7v9 ). Il semblerait que les choses aient peu évoluées depuis.

    Au final je préfère depuis la convention *ITest et failsafe même s’il faut finter sous Eclipse (via infinitest par exemple) ou passer sur intelliJ. Quand on aime Maven on aime les conventions :p

  3. Publié par Bastien Bonnet, Il y a 3 années

    Salut Nathaniel. J’avais bien lu ton article (dont la portée est plus large que le mien), qui comporte des solutions alternatives intéressantes. Pour la date, c’est purement fortuit !

    La difficulté de lancer les tests unitaires séparément sous certains EDI n’est pas la seule raison pour laquelle j’ai recherché une solution alternative :
    – je n’aime pas avoir les tests d’intégration au même endroit que les tests unitaires ;
    – je n’aime pas être contraint de choisir ma convention de nommage (ex. *ITest.java) pour faciliter le filtrage plus tard.

  4. Publié par Sylvain, Il y a 3 années

    Je trouve pour ma part que maven a déjà bien normalisé la notion des tests d’intégration. C’est une phase à part dans le build et il y a le plug-in maven-failsafe-plugin.

    Si on respecte la convention de maven-failsafe-plugin pour nommer les tests d’intégration (**/IT*.java, **/*IT.java, **/*ITCase.java) alors « maven test » n’exécute que les tests unitaires et si on veux lancer les tests d’intégration on utilise « maven verify ». Pas de configuration spécial dans le pom, c’est par défaut.

    Je concède que si on veut lancer via l’IDE, là, cette solution me semble plus élégante.

  5. Publié par Sebastien Lorber, Il y a 3 années

    On peut aussi utiliser l’annotation @Category si on utilise JUnit.

    Dans mon ancienne équipe on utilisait ca avec « mvn clean install -Pit » avec une config spécifique du plugin surfire sur ce profil maven. Perso je trouve ça mieux que des regex et de toute facon il faut souvent un profil maven différent (par ex pour lancer un serveur jetty embedded pour tester une API)

  6. Publié par Clément HELIOU, Il y a 3 années

    Bastien,

    Je trouve votre solution relativement élégante et vous rejoins à propos des règles de nommage.
    Je trouve que filtrer sur un nom de classe est bien peu robuste par rapport aux dérives possibles. A moins de vérifier à coup sûr le nom des classes de tests d’intégration…

    Par ailleurs, et pour compléter ce que disait Sébastien, il existe un attribut « groups » dans l’annotation « @Test » de TestNG qui, couplée à l’usage de Surefire et d’un profil, permet d’exclure/inclure les tests d’intégration. J’en fais mention dans un article de mon blog (http://mister-tie.fr/blog/2013/07/24/testng-peut-il-detroner-junit-13/) où je compare JUnit @ TestNG.

    Bien à vous.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *