Distribuer les tests JUnit avec Gridgain et Maven
Dans un précédent article, Meriem Berkane introduisait le build distribué et notamment la distribution des tests sur des agents. TestNG propose cette fonctionnalité de base, JUnit par contre ne le fait pas. Mais Gridgain vient combler ce manque depuis sa version 1.6. Dans cet article, je vais donc mettre en oeuvre Gridgain dans un build Maven afin de pouvoir, par la suite, l'utiliser sur le serveur d'intégration continue.
Gridgain
Gridgain est un framework open source permettant de développer des applications java en cloud ou en grille. Basé sur des annotations, il permet depuis la version 1.6 de distribuer les tests :
public class GridTests extends TestSuite {
@GridifyTest
public static Test suite()
{
TestSuite suite = new TestSuite("Test for com.octo.gridgain");
suite.addTestSuite(ClientDaoTest.class);
// ...
}
}
L'annotation @GridifyTest
permet d'exécuter la suite de tests sur la grille. Si vous n'aimez pas les annotations, vous pouvez utiliser GridJunit3TestSuite
au lieu de la simple TestSuite
.
C'est censé fonctionner avec JUnit 4 également (GridJunit4Suite
) mais j'ai essuyé quelques exceptions m'empêchant d'aller au bout...
Le code et les tests sont envoyés sur chaque nœud automatiquement selon l'algorithme Round Robin. Vous pouvez également réécrire votre méthode de répartition.
Alors bien sûr, ce n’est pas magique et pour distribuer les tests, il faut bien un ou des nœuds sur lesquels les distribuer. Plusieurs possibilités :
- Dans un mode simple et un peu artisanal, vous disposez dans le répertoire bin de l’installation de gridgain, d'un fichier gridgain-junit.bat (ou .sh). Lancez le autant de fois que vous souhaitez de nœuds sur une ou plusieurs machines. C'est assez laborieux et si on lance tout sur la même machine, on fini par plomber le temps de test.
- Pour pallier à ça, on peut envisager de lancer le script gridgain au démarrage de la machine en tant que service.
- Dans un mode un peu plus industrialisé, vous pouvez exploiter des serveurs d'application existants en y installant gridgain. Ainsi, gridgain se lancera en même temps que le serveur d'app et sera prêt à traiter des tests tant que le serveur sera up. Par exemple, j'ai testé l'installation de gridgain dans un JBoss. C’est assez simple à mettre en place et ça fonctionne très bien.
Gridgain peut découvrir les nœuds faisant partie de la grille grâce au mécanisme d'IP multicast. Ça simplifie beaucoup le travail, gridgain se charge de soumettre les paquets et toute machine capable de les recevoir (gridgain installé) se verra remettre des tests à exécuter.
Une fois l'infrastructure prête, plusieurs choses restent à configurer pour exécuter les tests. Et plutôt que de le faire dans Eclipse comme la vidéo d'exemple le montre, je l'ai fait dans Maven (toujours dans l'optique de mon build continu)...
Maven
Dépendances
Avant de rentrer dans les détails techniques, quelques généralités. Tout d'abord, les dépendances de build :
<repositories>
<repository>
<id>gridgain</id>
<name>GridGain Repository</name>
<url>http://www.gridgainsystems.com/maven2/</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.gridgain</groupid>
<artifactid>gridgain</artifactid>
<version>2.1.1</version>
<scope>test</scope>
</dependency>
</dependencies>
Il faut ajouter le repo gridgain car les dépendances ne sont pour le moment pas dans le repo central.
Compilation 1.5
Compilation en 1.5 pour le support des annotations :
<build>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-compiler-plugin</artifactid>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
<!-- ... -->
</plugins>
</build>
Exécution de la suite sur la grille
<properties>
<aspectjweaverversion>1.6.4</aspectjweaverversion>
</properties>
<build>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-surefire-plugin</artifactid>
<version>2.4</version>
<configuration>
<!-- (1) -->
<includes>
<include>**/*GridTests.java</include>
</includes>
<!-- (2) -->
<argline>-javaagent:${project.build.directory}/libs/aspectjweaver-${aspectjweaverversion}.jar</argline>
<workingdirectory>${project.build.directory}/libs/</workingdirectory>
</configuration>
</plugin>
<!-- (3) -->
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-dependency-plugin</artifactid>
<executions>
<execution>
<id>copy</id>
<phase>process-resources</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactitems>
<artifactitem>
<groupid>org.aspectj</groupid>
<artifactid>aspectjweaver</artifactid>
<version>${aspectjweaver-version}</version>
<outputdirectory>${project.build.directory}/libs/</outputdirectory>
</artifactitem>
</artifactitems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
- Premièrement, j'exclue les tests pour n'ajouter que la ou les suites de tests (effectivement, c'est la suite qui est gridifiée).
- Ensuite, on a besoin de préciser le javaagent, en l'occurence aspectj weaver pour instrumenter le code annoté et l'exécuter sur la grille plutôt qu'en local.
- Et pour que le javaagent retrouve ses petits, il faut copier le jar dans le répertoire pointé par le javaagent.
Un point important qu'on ne voit pas dans le pom est la présence du META-INF/aop.xml (disponible dans gridgain-x.x.x/config/aop/aspectj/) dans les ressources du projet (src/test/resources). Effectivement ce répertoire doit être dans le classpath. Il permet de sélectionner les portions de code qui seront tissés et donc susceptibles d'être exécutées sur la grille gridgain.
Exécution
Comme je le disais plus haut, il faut utiliser le .bat pour simuler des nœuds ou avoir une vraie grille. Ensuite, depuis votre projet maven, lancez simplement un mvn test -DGRID_ROUTER_PREFER_REMOTE=true
------------------------------------------------------- T E S T S
Running com.octo.gridgain.GridTests [00:56:01,906][INFO ][main][GridKernal%junit] _____ _ _ _____ _ / ____| (_) | |/ ____| (_) | | __ _ __ _ __| | | __ __ _ _ _ __ | | |_ | '__| |/ _` | | |_ |/ _` | | '_ \ | |__| | | | | (_| | |__| | (_| | | | | | \_____|_| |_|\__,_|\_____|\__,_|_|_| |_|
...
[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESSFUL [INFO] ------------------------------------------------------------------------
Et voilà ! On a un build Maven qui exécute les tests en parallèle sur plusieurs nœuds (dans les logs jboss ou sur la console du bat, vous pouvez voir les tests exécutés par le nœud en question).
Conclusion
Écrire des tests, c'est bien... Les exécuter, c'est mieux ! Et donc si vous avez arrêté de les exécuter car vous êtes perpétuellement en train d'attendre qu'ils soient terminés, gridgain peut vous aider en répartissant l'exécution sur une grille.
Je ne l'ai pas précisé avant mais il va de soit que les tests doivent être le plus unitaires, optimisés et indépendants possibles. Donc dans un premier temps, envisagez d'optimiser vos tests, et si les problèmes persistent, songez à gridgain et gardez en tête que les tests ne doivent pas être dépendants.
Sur l'intégration continue, cela peut également être utile pour alléger la charge sur le serveur. L'idéal serait de pouvoir combiner cette fonctionnalité avec les possibilités de build distribué fournies par les principaux acteurs du marché (Teamcity, Hudson, ...) et ainsi lancer les tests sur les agents déjà configurés. Mais pour peu que le réseau soit capable de gérer l'IP Multicast (sur du LAN de préférence), vous devriez déjà pouvoir distribuer les tests sur différents nœuds en laissant Gridgain découvrir les noeuds participants à la grille...