Les frameworks mutants ou comment évaluer la qualité de ses tests unitaires

De tous les outils de l’amélioration continue pour veiller à une bonne qualité de code, l’un des plus populaires, des plus simples (et probablement mon préféré) est l’utilisation des tests automatisés (unitaires et autre).
Or, il ne suffit pas d’écrire des tests unitaires. Ceux-ci doivent aussi se révéler efficaces.

Le TDD (Test Driven Development) ou BDD (Behavior Driven Design) peuvent être des solutions à ce souci d’efficacité.
Cependant, les tests unitaires sont sujets à grossir, être modifiés par différentes personnes, …
D’une manière générale, ils peuvent facilement échapper à notre contrôle.
Il serait alors tellement agréable d’avoir un moyen automatisé de les évaluer.
Le taux de couverture de test n’est pas une métrique suffisante puisqu’il permet simplement de se donner une idée de là où passent ou ne passent pas les tests mais ils ne prévalent pas sur la pertinence du test et encore moins sur les vérifications effectuées en fin d’exécution. Qui n’a pas déjà vu un Assert.assertTrue(true) ?

Une solution pourrait alors être dans les frameworks dit “mutation testing” frameworks.
Il s’agit d’outils qui :

  1. créent des variations d’un code métier, variations appelées “mutants”
  2. lancent les tests unitaires sur ces mutants

Si les tests continuent à passer alors que le code a changé (mutation), cela signifie que les tests ne sont pas pertinents.
Ces variations peuvent être diverses : changement d’opérateurs (opérateur + en -), suppression d’une instruction, inversion d’une égalité (== devient != dans un if), …

Le mutation testing par l’exemple

Un outil que j’ai trouvé récemment pour faire du mutation testing, c’est Humbug.
Humbug me semble être le seul projet viable en PHP dans le sens où il est toujours maintenu et utilisé par différents projets.
Humbug fonctionne avec PHPUnit, framework de tests le plus répandu dans le monde PHP. 
Son installation se fait avec Composer, le gestionnaire de paquets de PHP :

composer global require humbug/humbug

L’installation peut aussi se faire avec Git ou Phar.
Après ça, on peut alors configurer Humbug pour un projet donné :

humbug configure

Humbug nous posera alors des questions qui permettront de définir des informations comme :

  • Quel code métier faire muter ?
  • Où générer le rapport ?

On peut alors lancer Humbug :

humbug

Si tout s’est bien passé, on obtient alors un résultat comme suit :
résultats ok humbugLà, tous les indicateurs sont bons.
Maintenant, pour illustrer l’intérêt du framework, je rajoute dans mon code une fonction d’addition, sans test associé :

function add($number, $number2) {
    return $number+$number2;
}

Et là, j’obtiens :
Résultats avec des lignes non couvertes HumbugLe rapport m’indique que j’ai des lignes non couvertes par mes tests.
Le fichier texte de résultats d’exécution les détaille plus précisement sous la forme :
Uncovered
1) Mutator \Humbug\Mutator\Arithmetic\Addition on \PhpSchool\Php7Way\Exercise\NullItsNull::add() in /usr/share/nginx/html/test/php7-way/src/Exercise/NullItsNull.php on line 52
Bon, du coup on aurait pu avoir ce résultat avec la couverture de tests.
Plus intéressant encore, je vais rajouter un test non efficace pour tester cette fonction. Ce test vérifie que le retour de la fonction est bien de type “integer” mais ne vérifie pas que les nombres ont bien été additionnés et que le résultat est correct.

use PHPUnit_Framework_Constraint_IsType as PHPUnit_IsType;
/**
* @test
*/
public function shouldGetAnIntegerAfterAddingTwoNumbers()
{
  $testedClass = new NullItsNull();
  $additionResult = $testedClass->add(1, 2);
  $this->assertInternalType(PHPUnit_IsType::TYPE_INT, $additionResult);
}

Le résultat, cette fois-çi est le suivant :
Résultats avec des mutants non tués humbugC’est dans le fichier texte qu’on trouve encore une fois le détail avec ceci :
Escapes
1) \Humbug\Mutator\Arithmetic\Addition
Diff on \PhpSchool\Php7Way\Exercise\NullItsNull::add() in /usr/share/nginx/html/test/php7-way/src/Exercise/NullItsNull.php:

--- Original
+++ New
@@ @@
   {
-        return $number+$number2;
+        return $number-$number2;
   }
}

Humbug nous montre le mutant créé. Il a en fait pris l’opérateur + de la fonction initiale et l’a remplacé par un opérateur -. Ainsi, le mutant a fait de la fonction d’addition originale une fonction de soustraction.
Les tests ont continué de passer, ce qui prouve leur non pertinence.
Les différentes mutations qu’effectue Humbug sont  présentées dans sa documentation.

Les avantages et les inconvénients d’un outil de mutation testing

Il est extrêmement important, non seulement de tester son application, mais aussi de bien la tester. Et c’est là tout l’apport d’un framework de mutation testing qui automatise une partie de l’évaluation de la qualité de nos tests.
L’inconvénient majeur d’un framework de mutation testing est le temps d’exécution nécessaire à son analyse. En effet, la création des mutants peut prendre du temps en fonction de la quantité de code métier et des possibilités de mutation. C’est pourquoi ce sont des outils qui ne sont pas nécessairement destinés à être lancés à chaque build de l’intégration continue, mais plutôt sur des créneaux hebdomadaires.

Les avantages et inconvénients de Humbug

C’est un outil clair, facile à installer et utiliser.
Cependant, Humbug est la seule référence en PHP et ne fonctionne qu’avec PHPUnit.
Personnellement par exemple, je n’ai pu tester l’outil que sur mes petits projets personnels faits avec PHPUnit, mais pas sur mes gros projets d’entreprise faits avec Atoum.
Mais après tout, le projet est open source et amené à évoluer. Il est possible d’y contribuer grâce aux pull requests.
Les outils de mutation testing se retrouvent dans l’ensemble des langages informatiques avec PIT pour Java ou grunt-mutation-testing en JavaScript.

Auteur/Autrice

Une réflexion sur “Les frameworks mutants ou comment évaluer la qualité de ses tests unitaires

Laisser un commentaire

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

%d blogueurs aiment cette page :