Une suite de tests au vert rassure. Tous les tests passent, donc le code doit être bon. Mais une suite qui passe vous dit seulement que rien de ce que les tests vérifient n'est brisé. Elle ne dit rien de ce qu'ils ne vérifient jamais. Alors nous testons les tests, en cassant notre propre code exprès.
Un test qui ne peut pas échouer n'est pas un test
Imaginez une règle : il faut avoir seize ans pour détenir un compte. Quelqu'un écrit le code et un test. Le test vérifie qu'une personne de trente ans est acceptée et qu'une personne de dix ans ne l'est pas. Il passe.
Changez maintenant un seul caractère dans la règle :
// la règle
const mayHoldAnAccount = (age: number) => age >= 16;
// la même règle, cassée exprès
const mayHoldAnAccount = (age: number) => age > 16;Une personne de seize ans est maintenant refusée. Le test s'en aperçoit-il ? Non. Trente ans passe encore, dix ans est encore refusé, et la suite reste au vert. La règle est fausse, et rien ne nous l'a dit.
Les tests de mutation, en mots simples
Cette petite faille volontaire s'appelle un mutant. Faire des tests de mutation, c'est en créer beaucoup, un à la fois, et lancer les tests contre chacun :
- Changer une petite chose dans le code : inverser une comparaison, retirer une vérification, remplacer une valeur.
- Lancer les tests.
- Si un test échoue, le mutant est attrapé. Tant mieux.
- Si tous les tests passent encore, le mutant a survécu, et il manque un test. Nous l'écrivons.
Pour l'exemple, il suffit d'un test de plus : une personne de seize ans est acceptée. Avec ce test en place, la règle cassée fait passer la suite au rouge immédiatement.
Comment nous nous en servons
Cet ordre compte. Un test que nous n'avons jamais vu échouer ne teste peut-être rien du tout. Le voir passer au rouge d'abord prouve qu'il attraperait la faille pour laquelle il a été écrit.
Les mutants ne sont pas la seule vérification. Chaque changement passe aussi par la suite de tests complète. Nous donnons à certaines parties du code de grandes quantités de données aléatoires, pour trouver les cas que personne n'a pensé à écrire. Un serveur de vérification relance les suites, une partie des mutations et des recherches de secrets divulgués selon un horaire, puis signale ce qu'il trouve. À la fin de chaque phase, des réviseurs font tout pour casser ce que nous avons bâti avant que nous le disions terminé.
Ce que ça ne prouve pas
Les tests de mutation vous disent que vos tests remarqueraient les failles que vous avez plantées. Ils ne peuvent pas prouver que le code n'a aucune faille, et ils ne remplacent pas un audit indépendant. C'est pourquoi, sur le suivi, tout ce qui attend un audit ne peut pas paraître terminé, peu importe le nombre de tests qu'il passe.
Pourquoi ça vous concerne
Quand un élément de travail avance sur notre suivi, c'est qu'un dépôt a changé, et que ce changement est d'abord passé par ces vérifications. Personne ne peut taper un chiffre. Nous préférons vous montrer une barre plus lente et vraie qu'une barre plus rapide et fausse.