Tests unitaires sous Maven

Tester les applications avec Maven

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

Maven permet, comme Apache Ant, d'exécuter les tests unitaires et de consulter ses résultats sous un format souhaité (fichier ou output directemenet dans la console). Cependant, quelques problèmes peuvent se produire lors du transfert des tests sous Maven. On les découvrira à travers cet article.

Comme on l'a indiqué dans l'article consacré au transfert d'un projet sur Maven, Maven impose une structure propre à la suite des tests automatiques. Juste au titre d'un bref rappel, les tests doivent se situer dans le répertoire /src/test et reprendre la même structure que les classes utilisées par l'application. On aura donc un tel sous-repértoire des tests :

   |---test
   |-----java
   |-------com
   |---------example
   |-----------library
   |-------------model
   |---------------TestEntity.java
   |-------------AbstractControllerTest.java
   |-------------BookingControllerTest.java
   |-------------NewsletterSenderAsyncTest.java
   |-------------SpelTest.java
   |-------------SubscriptionControllerTest.java
   |-------------SubscriptionTest.java
   |-------------SuggestionControllerTest.java
   |-------------ToolTest.java
   |-------------XSSCleanerTest.java
   |-----resources
   |-------test-config.xml

Exécuter les tests unitaires sous Maven

Pour lancer les tests unitaires sous Maven il faut utiliser la commande mvn clean test. En fait, elle fait appel à des méthodes. La première fonction supprime tous les fichiers générés et les recompile une nouvelle fois. La deuxième lance les tests spécifés dans notre dossier /src/test/java/com/example/library.

Après l'exécution de cette commande, on verra sur l'écran les erreurs qu'on résoudra plus loin dans l'article.

Mauvais package JavaEE sous Maven

La première exécution du test retournera une erreur du type :


	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:68)
testValidBooking(com.example.library.test.BookingControllerTest)  Time elapsed: 1.312 sec  <<< ERROR!
java.lang.ClassFormatError: Absent Code attribute in method that is not native or abstract in class file javax/persistence/PersistenceContextType
	at java.lang.ClassLoader.defineClass1(Native Method)

Il est provoqué par le fait que le JAR avec JavaEE, chargé initialement depuis Maven (on suppose que <scope>provided</scope> est absent dans le pom.xml pour les dépendances). Et ce package n'est qu'un API qui contient les noms des classes et interfaces sans leurs contenus.

Pour rémedier à ce souci, il suffit de télécharger l'archive JavaEE sur le site du Java et l'installer manuellement dans le répertoire local du Maven. L'installation dans le répertoire local s'effectue grâce à la commande mvn install:install-file -Dfile=./javaee-16.jar -DgroupId=javax -DartifactId=javaee-api -Dversion=16 -Dpackaging=jar. Les attributs groupId, arrtifactId et version servent à déterminer quelle dépendance doit être récupérée dans pom.xml.

Exclure les classes des tests sous Maven

Parfois on veut exclure certaines classes de notre suite des tests. En occurrence, on ne voudrait pas que la classe de configuration, AbstractControllerTest, soit lancée. Le lancement provoquera un message d'erreur :

Running com.example.library.test.AbstractControllerTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.074 sec <<< FAILURE!
initializationError(com.example.library.test.AbstractControllerTest)  Time elapsed: 0.024 sec  <<< ERROR!
java.lang.Exception: No runnable methods
        at org.junit.runners.BlockJUnit4ClassRunner.validateInstanceMethods(BlockJUnit4ClassRunner.java:171)
        at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:115)
        at org.junit.runners.ParentRunner.validate(ParentRunner.java:269)
        at org.junit.runners.ParentRunner.<init>(ParentRunner.java:66)
        at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:59)
        at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.<init>(SpringJUnit4ClassRunner.java:104)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:31)
        at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:24)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
        at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:29)
        at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:57)
        at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:24)
        at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:262)
        at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
        at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:124)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray2(ReflectionUtils.java:208)
        at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:159)
        at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:87)
        at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:153)
        at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:95)

Dans ce cas il faut donc exclure la classe dans le plugin qui s'occupe de lancer les tests sous Maven. Le plugin s'appelle Maven Surefire Plugin. Il exécute les tests mais aussi se charge de générer les résultats, soit sous forme des fichiers texte, soit directement dans la console. Les résultats sont placés dans le répertoire /target/surefire-reports.

Regardons maintenant comment configurer ce plugin pour qu'il n'exécute pas de tests pour la classe AbstractControllerTest :

  <build>
    <finalName>LibrarySpringSample</finalName>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.14.1</version>
        <configuration>
          <excludes>
            <exclude>**/AbstractControllerTest.java</exclude>
          </excludes>
        </configuration>
      </plugin>
    </plugins>
  </build>

La configuration est assez explicite. La balise <exclude /> peut contenir des enfants-balises <exclude /> qui déterminent les classes à exclure des tests. Les classes peuvent être déterminées directement ou via une expressions régulière. Plus d'informations à ce sujet se trouve dans la documentation du Maven Surfire.

Maven est un outil puissant qui permet de piloter un projet, aussi bien ses dépendances que ses tests. Dans cet article on a présenté comment implémenter les tests unitaires JUnit sous Maven et à quels problèmes on peut faire face pendant le processus.

Bartosz KONIECZNY Maven

Une question ? Une remarque ?

*

*

Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil Android

Comment ajouter un JAR externe à notre application développée sous Eclipse ?

Dans l'onglet Package Explorer faire un click droit sur le projet en question. Ensuite sélectionner Properties, se déplacer vers le menu Java Build Path. Dans cette partie choisir l'onglet Libraries et cliquer sur le bouton Add jar. A la fin de l'opération il suffit appliquer le choix en cliquant sur Ok.