Configuration d'un serveur d'intégration continue - [Partie 4] - NCover
By Michael DELVA on Tuesday 19 January 2010, 16:58 - Miscellaneous - Permalink
TweetPartie 4 de cette série d'articles, qui va cette fois se concentrer sur la couverture du code par les tests unitaires, grâce à un outil nommé: NCover. Je ne rentre dans dans les détails sur ce qu'est capable de faire cet outil, leur site est très fourni à ce niveau là. Par contre pour ceux qui ne connaissent pas du tout, cet outil va vous permettre de lancer vos tests unitaires, et va analyser durant l'exécution des tests quelles sont les fonctions de votre code à tester qui sont appelées, et surtout celles qui ne le sont pas. Ce qui permet de savoir où orienter les tests unitaires afin de couvrir l'ensemble du code.
Le fichier NAnt
C'est devenu une habitude maintenant, nous allons intégrer l'analyse de la couverture du code dans notre fichier NAnt dans un premier temps, avant de configurer Hudson par la suite pour afficher le rapport.
Pour cette première étape, ce n'est pas très difficile, puisqu'il existe une aide en ligne très pratique sur le site de ncover, où on y apprend tout d'abord qu'ils ont créé une tâche NAnt à charger dans le fichier XML (la liste exhaustive des options se trouvant ici), et dont on n'a que les paramètres à remplir pour que ça fonctionne, et où il y a un exemple d'utilisation de cette tâche avec Gallio, qui se trouve comme de par hasard être le framework de tests qu'on a utilisé dans la deuxième partie de cet article.
La modification du fichier NAnt va se faire en 2 parties. Nous allons tout d'abord commencer par générer le fichier de couverture de code, en utilisant ncover.console.exe, puis ensuite générer les fichiers de rapport en utilisant ncover.reporting.exe
NCover.Console
Concrètement, ce que nous allons faire est simple: nous allons utiliser la tâche NAnt de NCover pour appeler Gallio.Echo, afin qu'il exécute les tests de nos assemblies. On va passer à la console de Gallio les même paramètres que lors de la partie 2 de ce tutoriel afin qu'il génère le rapport de tests (donc oui nous allons générer 2 rapports en une seule passe). Et on utilise comme paramètres pour NCover les même que ceux donnés sur leur site.
Vous pourrez remarquer l'utilisation de 2 paramètres supplémentaires à la tâche NCover: includeAssemblies et excludeAssemblies. La raison en est très simple: si on ne spécifie pas ces paramètres, NCover va générer un rapport de couverture de code qui va se baser sur l'analyse de toutes les assemblies utilisées pour compiler les tests unitaires. Vous aurez donc donc non seulement le rapport des analyses sur la couverture de votre code, mais aussi, ce qui est plutôt inutile, sur la couverture du code de tests. Mais également du code de xUnit, de votre outil de mocking, ou de Dependency Injection, ce qui est carrément une perte de temps, vu que vous ne modifiez pas les sources de ces outils.
Donc avec includeAssemblies, on spécifie à la console de NCover qu'on veut que le rapport ne concerne que nos assemblies, et avec excludeAssemblies on exclut nos assemblies de tests. Seules donc les assemblies des programmes que l'on va distribuer sera analysé.
<property name="ncover.dir" value="C:\Program Files\NCover" />
<property name="ncover.nanttasks.name" value="NCover.NAntTasks.dll" />
<property name="ncover.console.name" value="ncover.console.exe" />
<property name="ncover.console.path" value="${ncover.dir}\${ncover.console.name}" />
<loadtasks assembly="${ncover.dir}\Build Task Plugins\${ncover.nanttasks.name}" />
<target name="run.tests.and.coverage" depends="compile.sources, move.assemblies">
<echo message="Starting to run Code Coverage" />
<ncover program="${ncover.console.path}"
testRunnerExe="${gallio.echo.path}"
testRunnerArgs="${tests.assemblies} /rt:xml /v:quiet /ne /rd:${tests.output.dir} /rnf:${gallio.echo.report.name} /hd:${tools.tests.assemblies.output.dir}"
workingDirectory="."
coverageFile="${ncover.coverage.file.path}"
symbolSearchLocations="Registry, SymbolServer, BuildPath, ExecutingDir"
coverChildProcess="gallio.host.exe"
includeAssemblies="Emidee.*.dll"
excludeAssemblies="Emidee.*.Tests.dll"
projectName="Basic" />
<call target="run.coverage.reporting" />
</target>
Vous remarquerez, à la fin de la tâche, l'appel à une autre tâche, run.coverage.reporting, que je vais vous expliquer tout de suite.
NCover.Reporting
Générer le rapport de NCover se fait en 2 étapes. La première, que nous venons d'expliquer, va générer un fichier XML depuis l'analyse des tests unitaires. La seconde partie va analyser ce fichier XML et va générer dans un dossier toute une série de fichiers HTML qui contiendront une explication graphique et détaillée de la couverture du code ce chaque assembly / namespace / classe / fonction, ce qui vous permettra en un coup d'oeil de repérer immédiatement quelles sont les tests unitaires à créer pour combler les manques.
Pour l'écriture de cette tâche dans le fichier NAnt, je vais encore une fois suivre l'exemple donné sur le site de NCover:
<property name="ncover.merge.name" value="coverage.merge.xml" />
<property name="ncover.merge.path" value="${ncover.output.dir}\${ncover.merge.name}"/>
<property name="ncover.reporting.name" value="ncover.reporting.exe" />
<property name="ncover.reporting.path" value="${ncover.dir}\${ncover.reporting.name}" />
<property name="ncover.reporting.trend.dir" value="../.." />
<property name="ncover.reporting.trend.file.name" value="ncover.reporting.trend" />
<property name="ncover.reporting.trend.file.path" value="${ncover.reporting.trend.dir}/${ncover.reporting.trend.file.name}" />
<target name="run.coverage.reporting">
<echo message="Starting NCoverExplorer report generation" />
<ncoverreporting program="${ncover.reporting.path}"
projectName="Basic"
outputPath="${ncover.output.dir}"
mergeFileName="${ncover.merge.path}"
coverageTrendPath="${ncover.reporting.trend.file.path}">
<coverageDataPaths>
<include name="${ncover.coverage.file.path}" />
</coverageDataPaths>
<reports>
<report reportType="FullCoverageReport" format="Html" />
</reports>
</ncoverreporting>
</target>
On spécifie donc le chemin vers le fichier XML généré par NCover.console (via le noeud coverageDataPaths), et on crée un rapport complet au format HTML (<report reportType="FullCoverageReport" format="Html" />) dans le répertoire de destination ${ncover.output.dir}. On crée aussi un fichier Trend (qui sera en fait créé une seule fois, et modifié à chaque génération du rapport), que l'on pourra ouvrir avec NCover.Explorer (l'interface graphique de NCover) afin de visualiser les graphiques de tendance directement. Comme ce fichier ne doit pas être supprimé à chaque build par Hudson, je le créé 2 niveaux au dessus du répertoire courant de NAnt (le dossier Tools de la solution), c'est à dire dans le dossier du job, dans le répertoire de Hudson.
Il existe pas mal de paramètres supplémentaires à cette tâche, comme par exemple la possibilité de définir des seuils de couverture du code au dessous desquels ncover.reporting retournera un code d'erreur faisant échouer le build. Vous pourrez trouver plus d'informations à cette page.
Configuration de Hudson
Comme pour les parties précédents, maintenant que nous avons configuré notre fichier NAnt, il nous reste désormais à configurer Hudson, une nouvelle fois grâce à un plugin. Rien de bien compliqué, j'ai juste suivi la procédure décrite ici.
On commence par installer le plugin ncover de Hudson, puis on va configurer le job, comme indiqué sur le lien précédent:
Puis quand vous lancez un build, vous avez un lien Code Coverage
qui vous mène vers les rapports:
Conclusion
Une étape de plus dans la configuration de notre serveur d'intégration vient d'être réalisée. Nous sommes désormais capables de visualiser très simplement la couverture du code par nos tests unitaires, ainsi que la progression de cette couverture de code de build en build. Il reste encore une dernière partie à traiter dans cette série d'articles. Elle portera sur Hudson uniquement, et nous y verrons quelques petits plugins très utiles qui nous permettront d'avoir quelques informations supplémentaires importantes sur la compilation du projet, comme un résumé des tâches restantes à accomplir dans le code, ou la liste des avertissements générés par le compilateur.