Couverture de code : Mise en œuvre de Cobertura pour une application Web
Ce billet présente la mise en œuvre de Cobertura pour une application Web. Vous y trouverez une rapide présentation de Cobertura, suivie d’un tutorial pour son utilisation dans le cadre des tests d’une application Web s’exécutant sur un serveur Tomcat.
Présentation de Cobertura
Cobertura est un outil qui permet de mesurer la couverture de code d’un jeu de tests. Il ne permet pas de réaliser les tests, ni de les automatiser : Cobertura mesure juste la pertinence et la complétude de ces tests.
Les outils qui mesurent la couverture de votre code Java fonctionnent tous de la même manière :
- il n’est pas nécessaire de modifier le code Java : les solutions ne sont pas intrusives.
- il y a tout d’abord une phase d’instrumentalisation des fichiers compilés (class, jar ou war) : cette phase qui a lieu après la compilation semble construire une couche de proxies sur les binaires existant afin d’intercepter tous les appels.
- on exécute les tests avec les binaires instrumentalisés en ajoutant un jar nécessaire au bon fonctionnement de la couche de proxy.
- on termine le test par une sortie violente (Ctrl C souvent) afin que la couche de proxy reprenne la main est crée le fichier de compte rendu
- hors exécution, on transforme le fichier de compte rendu en pages HTML : on obtient un site qui montre pour chaque classe java : le pourcentage de code exécuté, le nombre de branches conditionnelles exécutées et le détail du code exécuté ou non.
Le résultat du processus est donc un rapport qui indique si la totalité du code a été testé et qui permet la détection du code non exécuté.
Ci dessous, un exemple de la page index.html qui est la synthèse du rapport est le point de départ des différentes pages d’analyse.
On constate que pour chaque package, on a une synthèse du code couvert :
- on a le nombre de classes composant le package
- on a le pourcentage de lignes de code exécutées (les lignes de commentaire ne sont pas comptabilisées)
- on a le nombre de lignes exécutées et le nombre total de lignes de code de ce package (toutes classes confondues)
- ce que Cobertura appelle branch est tout branchement conditionnel (if ,else…) : il mesure si pour chaque test, on a eu à le cas du true et du false. Si vous n’avez eu que le cas du true, vous ne serez qu’à 50% du test de couverture des branchs.
- la dernière colonne est la complexité “cyclotimique” du code (j’avoue ne pas avoir utilisé cette information)
Pour une classe, il est possible d’avoir le détail. Ce point peut être très utile quand on essaie d’améliorer ses tests : il faut comprendre quelle partie du code n’a pas été testé afin de construire le test qui la couvrira.
On constate que l’on a en haut, une synthèse de la couverture de la classe avec son pourcentage et le nombre de lignes testées sur le nombre de lignes totales de la classe.
On a ensuite le code, en vert les lignes exécutées avec le nombre de fois, en rouge les lignes qui ne l’ont pas été : dans l’exemple ci-dessus seuls le constructeur et la méthode getSolde ont été exécuté.
Mise en œuvre de Cobertura pour une application Web
La majorité des documentations présente l’utilisation de Cobertura dans les phases de tests unitaires jUnit. La mise en œuvre avec une application Web déployée sur un serveur d’application JEE comme Tomcat est plus délicate : le bon fonctionnement de Cobertura repose sur la bonne gestion du fichier de données (appelé datafile et dont le nom par défaut est cobertura.ser). Or cette gestion repose sur ce que le serveur d’application considère comme le point de départ de son système de fichiers et ce paramètre change énormément d’une configuration à l’autre et d’un serveur à l’autre.
Configuration du serveur Tomcat 6.0
Le test a été fait sur un serveur Tomcat 6.0. Le serveur est lancé avec le startup.bat et non pas comme service Windows : cela a un impact important sur l’endroit ou doit se trouver le fichier cobertura.ser.
Pour ma part, j’ai téléchargé depuis le site http://tomcat.apache.org/download-60.cgi la version core pour Windows 32 bits : 32-bits Windows.zip.
Le répertoire obtenu lors du unzip : apache-tomcat-6.0.29 a été copié dans un nouveau dossier placé sur mon bureau TestCouverture créé pour l’occasion. J’ai donc un environnement dédié pour mes tests.
J’ouvre le Catalina.bat qui se trouve dans Bin et j’ajoute en tout début de fichier la ligne suivante :
Set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_18
L’avantage de cette solution est de m’affranchir d’éventuelles restrictions de droits sur les répertoires : par défaut, Tomcat est souvent installé dans Program Files qui est soumis pour les versions récentes de Windows (Vista et Seven) a une politique de droits restrictive qui peut empêcher la mise à jour par Cobertura du fichier de données cobertura.ser.
Installation de Cobertura
J’ai utilisé la dernière version disponible (au moment de mes tests), la version 1.9.4.1.
Elle peut être téléchargée depuis le site suivant : http://cobertura.sourceforge.net/download.html
Il suffit de la dézipper dans un répertoire, dans mon cas le même dossier TestCouverture du bureau.
Instrumentalisation des classes avec Cobertura
Avant d’instrumentaliser mes classes de mon projet JEE Eclipse TestCobertura, je génère depuis Eclipse le war.
Je le déploie dans mon serveur Tomcat configuré pour le test : Bureau\apache-tomcat-6.0.29\webapps. Pensez aux éventuels jar qui doivent être installés dans Tomcat/lib comme ceux des drivers base de données.
J’exécute l’application après lancement du serveur Tomcat avec le startup.bat. Cela explose le fichier war TestCobertura. On arrête le serveur.
Pour instrumentaliser les classes java, on va partir du répertoire build du projet Eclipse qui contient les class et on va écraser ces mêmes class du répertoire explosé TestCobertura du serveur Tomcat (d’où l’importance de l’exécution de l’application) :
- Le répertoire de mon projet Eclispe est dans Mes Documents\eclipsewsp, les fichiers class sont donc dans : C:\Users\MONCOMPTEWINDOWS\Documents\eclipsewsp\TestCobertura\build\classes.
- Le répertoire du war explosé dans Tomcat est : C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\apache-tomcat-6.0.29\webapps\TestCobertura.
- Le fichier de données cobertura.ser devra être créé dans le répertoire bin de Tomcat : C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\apache-tomcat-6.0.29\bin
Pensez que :
- MONCOMPTEWINDOWS doit être remplacé par la chaine de caractères correspondant à votre configuration.
- TestCobertura est le nom de mon projet Eclipse.
- eclipsewsp est le nom de mon répertoire workspace Eclipse.
On ouvre un fichier Commande DOS. on se positionne dans le répertoire Cobertura :
cd C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\cobertura-1.9.4.1
On tape la commande suivante :
cobertura-instrument.bat --datafile "C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\apache-tomcat-6.0.29\bin\cobertura.ser" --destination "C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\apache-tomcat-6.0.29\webapps\TestCobertura\WEB-INF\classes" "C:\Users\MONCOMPTEWINDOWS\Documents\eclipsewsp\TestCobertura\build\classes"
Attention à bien utiliser les guillemets “ pour éviter les problèmes d’espaces dans d’éventuels noms longs. Le résultat devrait être :
Cobertura 1.9.4.1 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
Instrumenting 1 file to C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\apache-tomcat-6.0.29\webapps\TestCobertura\WEB-INF\classes
Cobertura: Saved information on 131 classes.
Instrument time: 4129ms
Dans le répertoire bin de votre serveur Tomcat, vous devriez trouver un fichier cobertura.ser. Ce fichier est nécessaire au bon fonctionnement de Cobertura : il indique la liste des classes qu’il doit instrumentaliser ainsi que le résultat des tests. Ce fichier devra être accessible en lecture/écriture durant l’exécution du test.
Si on souhaite pouvoir recommencer les tests à zéro, il est possible de sauvegarder cette version du fichier qui contient les classes instrumentalisées avec un compteur d’utilisation à zéro.
Les fichiers .class du répertoire WEB-INF\classes ont été modifié (leur date a été modifié).
Les classes sont instrumentalisées.
Test de l’application instrumentalisée
Si vous essayez de relancer l’application, vous allez avoir une erreur dans la log localhost :
Caused by: java.lang.NoClassDefFoundError: net/sourceforge/cobertura/coveragedata/HasBeenInstrumented
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(ClassLoader.java:632)
at java.lang.ClassLoader.defineClass(ClassLoader.java:616)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:141)
at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2733)
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1124)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1612)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1491)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at org.hibernate.util.ReflectHelper.classForName(ReflectHelper.java:192)
at org.hibernate.cfg.AnnotationConfiguration.parseMappingElement(AnnotationConfiguration.java:647)
... 57 more
Caused by: java.lang.ClassNotFoundException: net.sourceforge.cobertura.coveragedata.HasBeenInstrumented
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1645)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1491)
... 69 more
Cette erreur signifie qu’il manque le jar cobertura qui est utilisé par les classes instrumentalisées.
Dans la mesure, où l’on a construit un serveur Tomcat pour ses tests, il suffit d’ajouter cobertura.jar dans le répertoire bin de Tomcat.
Le jar cobertura se trouve dans le répertoire TestCouverture\cobertura-1.9.4.1. Il suffit de le copier dans TestCouverture\apache-tomcat-6.0.29\lib.
Vous pouvez redémarrer Tomcat avec Startup.bat et exécuté votre test.
A la fin de votre test, vous arrêtez Tomcat en vous mettant dans la console qu’il aura ouverte et tapez Ctrl C. Tomcat s’arrête et met à jour le fichier cobertura.ser avec les logs de votre utilisation.
Génération du rapport HTML
La phase finale est la génération du rapport au format HTML.
Le rapport utilise le fichier cobertura.ser mis à jour lors de votre utilisation. Pour que cela fonctionne, il faut que lors de l’exécution de votre application, Tomcat ait bien trouvé le fichier cobertura.ser créé lors de l’instrumentalisation. S’il ne le trouve pas, il créera un fichier cobertura.ser dans apache-tomcat-6.0.29\bin mais il ne contiendra aucune information.
L’emplacement de ce fichier dépend du mécanisme de lancement de tomcat : avec startup.bat, il se trouve dans apache-tomcat-6.0.29\bin.
Attention à ce que Tomcat puisse modifier ce fichier : cela sera probablement un problème si Tomcat s’exécute en service ou s’il se trouve dans un répertoire Program Files. C’est pour toutes ces raisons que je vous ai encouragé au début à installer votre propre version de test Tomcat.
Pour que le rapport est le maximum de détail, il faut disposer des sources.
Dans mon exemple, elles se trouvent dans le projet Eclipse sous : C:\Users\MONCOMPTEWINDOWS\Documents\eclipsewsp\TestCobertura\src.
On va générer le rapport dans le sous répertoire rapportCobertura du répertoire TestCouverture. Pour cela on crée le sous répertoire rapportCobertura
On se replace dans le répertoire TestCouverture\cobertura-1.9.4.1 et on tape la commande suivante :
cobertura-report.bat –source "C:\Users\MONCOMPTEWINDOWS\Documents\eclipsewsp\TestCobertura\src" --datafile "C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\apache-tomcat-6.0.29\bin\cobertura.ser" --destination "C:\Users\MONCOMPTEWINDOWS\Desktop\TestCouverture\rapportCobertura" "C:\Users\MONCOMPTEWINDOWS\Documents\eclipsewsp\TestCobertura\build\classes"
Pour information, les différentes options utilisées sont :
- source : le répertoire contenant les sources
- datafile : emplacement du fichier cobertura.ser
- destination : répertoire dans lequel on génère le rapport
Le dernier paramètre est l’emplacement des fichiers classes d’origine.
Attention à bien utiliser les guillemets “ pour éviter les problèmes d’espaces dans d’éventuels noms longs. Le résultat devrait être :
Cobertura 1.9.4.1 - GNU GPL License (NO WARRANTY) - See COPYRIGHT file
Cobertura: Loaded information on 23 classes.
Report time: 569ms
Dans le répertoire TestCouverture\rapportCobertura vous devriez voir une série de fichier html dont le fichier index.html.
Si le répertoire est vide, vérifiez le nom et le chemin de vos différents fichiers et répertoire. Le message est malheureusement le même entre un succès et un échec.
Pour consulter le rapport, double cliquer sur index.html.
Si quand vous consultez le détail d’une classe, vous n’avez pas le code de la classe mais le message
Unable to locate fr/natsystem/demonatjet/gettingstarted/DemoNatJet40.java. Have you specified the source directory?
C’est que vous avez oublié de spécifier l’option source de la commande ou bien que le nom du répertoire contenant vos sources est incorrect.
Cobertura et Spring et AspectJ
Cobertura et le module AspectJ de spring utilisent tous les deux des mécanismes de proxy qui entrent en collision. Il peut donc arriver, que lors de l’exécution d’une classe instrumentalisée, vous obteniez le message suivant (alors que cette même classe non instrumentalisée fonctionne parfaitement) :
org.springframework.beans.BeanWrapperImpl.convertForProperty(BeanWrapperImpl.java:462)
Dans mes fichiers de configuration Spring j’avais la déclaration suivante :
Pour Spring cela correspond à l’utilsation d’aspectJ avec le défaut suivant <aop:aspectj-autoproxy/>.
Pour résoudre le problème j’ai juste ajouté la ligne suivante en gras :
<aop:aspectj-autoproxy proxy-target-class="true" />
Cela a fait disparaitre mon erreur et j’ai pu continuer mes tests.
Conclusion
Nous avons fait un rapide tour de l’utilisation de Cobertura dans un contexte JEE. Vous pouvez maintenant valider si vos tests couvrent la totalité de votre code.
Le bon fonctionnement de ces tests reposent sur la capacité du serveur d’application à trouver le bon fichier cobertura.ser et à le mettre à jour. L’emplacement où un serveur d’application cherche se fichier reste pour moi un grand mystère. Dans le cas d’un serveur Tomcat démarré avec startup.bat il se trouve dans le répertoire Tomcat/bin. Dans les autres as, je vous laisse découvrir son emplacement. Une solution consiste à lancer votre serveur sur des classes instrumentalisées sans ajouter le fichier cobertura.ser. Il créera à la fin de l’exécution un nouveau fichier cobertura.ser vide mais qui vous indiquera son emplacement.
L’autre difficulté de l’utilisation avec un serveur d’application est le mécanisme de terminaison qui doit être utilisé. Cobertura semble placer une sorte de hook sur un certain type de sortie violente (le Ctrl C notamment). Il faut passer par ce hook, pour qu’il mette à jour le fichier cobertura.ser. Pour certaine configuration et certain serveur, l’arrêter en déclenchant ce hook peut être difficile à mettre en œuvre. Pour Tomcat, il suffit de démarrer avec Startup.bat et de l’arrêter avec Ctrl C.
Je prévois d’écrire rapidement, un second billet qui se concentrera sur l’opportunité et l'intérêt d’utiliser Cobertura dans un projet.
DiggIt!
Enregistrer sur Del.icio.us
0 commentaires:
Enregistrer un commentaire