Je vous ai, dans un premier article, présenté les principes des tests par mutations.
Dans celui-ci, je vais vous montrer quelques exemples d'application avec un outil permettant de contrôler la qualité de tests unitaires Java (JUnit, Test NG). Cet outil s'appelle PIT.
Pourquoi PIT ?
PIT est un outil qui permet de faire des tests par mutations sur des programmes java (5 et +).
Il existe d'autres projets java mais celui-ci semble être le seul actif. De plus, la plupart des autres outils sont lents, difficiles à utiliser et étaient plutôt destinés à des chercheurs plutôt qu'à des vraies équipes de développement.
PIT a l'avantage :
- d'être plus rapide. Il analyse en quelques minutes ce qui pouvait prendre des heures avec les outils plus anciens.
- d'être simple d'utilisation et industrialisable. Il fonctionne en effet avec ant, maven, gradle...
- d'être toujours développé et maintenu : dernière release en Février 2014 (v0.33).
- d'avoir des plugins pour Eclipse, IntelliJ et Sonar.
De plus, les rapports générés par PIT sont clairs et combinent couverture de code et scores par classes et méthodes testées (nombre de mutants tués / nombre de mutants générés).
Installation
Pour ajouter PIT à votre projet, vous devez tout d'abord ajouter ses dépendances Maven à votre pom.xml :
Vous devez aussi ajouter le plugin suivant:
Utilisation
PIT s'utilise en exécutant des commandes Maven. Deux goals sont proposés :
- le goal mutationCoverage (C'est celui-ci que j'utiliserai dans les exemples ci-dessous)
- le goal scmMutationCoverage
Le goal mutationCoverage
Le goal mutationCoverage permet d'analyser toutes les classes de votre application et de vos tests qui correspondent aux filtres définis dans le fichier pom.xml. Il peut être utilisé en exécutant la commande suivante :
mvn org.pitest:pitest-maven:mutationCoverage
Les rapports sont générés au format HTML dans le répertoire target/pit-reports/YYYYMMDDHHMI.
Le goal scmMutationCoverage
Le goal scmMutationCoverage permet de limiter l'analyse aux fichiers ayant un statut particulier dans le système de gestion de versions (SVN, Git). Par défaut elle ne s'applique qu'aux nouveaux fichiers et aux fichiers modifiés. Cela permet une analyse rapide avant de commiter ses fichiers vers le dépôt.
Exemple de commande :
mvn org.pitest:pitest-maven:scmMutationCoverage -Dinclude=ADDED,UNKNOWN -DmutationThreshold=85
Pour utiliser ce goal, le plugin Maven SCM doit être correctement configuré pour le projet.
Options
De nombreuses options sont disponibles, parmi lesquelles le nombre de threads à utiliser, ou les opérateurs de mutations. Elle sont détaillées ici.
Exemple d'utilisation
Soit a classe suivante qui permet de réaliser des opérations sur des personnes :
Et la classe de tests qui lui est associée :
Les tests passent et la couverture est de 100%. Génial !
Pourtant lorsque l'on lance PIT, on se rend compte que nos tests ne sont pas complètement pertinents. En effet, seulement 6 mutants sur 8 ont été tués. Autrement dit, sur 8 mutations créées, seules 6 ont été détectées par nos tests. Si nos tests avaient été plus complets/précis, ils auraient dû échouer pour chacune des mutations.
En cliquant sur le package, on accède à un rapport détaillé par classe testée :
Cet écran nous permet de voir la liste des mutations créées sur cette classe et nous indique si les mutations ont été détectées ou non.
Deux mutations n'ont donc pas été détectées (lignes 14 et 22).
La première concerne la méthode newPersonne(). PIT a supprimé la ligne 14 sans que les tests échouent. Cela nous montre que l'on a oublié de vérifier l'âge de la personne.
La seconde concerne la méthode isMajeur(). A la ligne 22, PIT a transformé l'opérateur >= en > ("changed conditionnal boundary") sans que nos tests échouent. Cela signifie que nous avons mal défini les bornes de notre jeu de données.
Voici nos tests une fois corrigés :
Et voici le nouveau rapport généré par PIT :
Maintenant, chaque mutation est détectée. PIT nous a permis d'améliorer nos tests.
Pour plus d’informations : http://pitest.org
Super outil! Merci pour ce retour. Après, il faut éviter de tomber dans le piège de vouloir tester trop bien et surtout vouloir tout tester, ca devient vite time consuming et peu de projet le permette. Par exemple, réserver l’utilisation de ce type d’outil pour les parties les plus sensibles/complexes du projet où le risque de régression est élevé.