Aller au contenu
AFUP AFUP Day 2025 Baromètre Planète PHP PUFA
 

Infection : Quand les X-men nous aident à mieux tester

Description

Ecrire des tests unitaires c'est super ! On ne démontre plus aujourd'hui leur utilité tant sur la partie "Documentation du code" que sur la partie "Test du code".

Mais comment être sûr•e que l'on a testé tous les cas de figure de l'utilisation d'une méthode? Y-a-t'il un moyen de tester nos tests unitaires ?

Je vous propose de répondre à cette question en vous parlant du mutation testing au travers d'un des outils PHP mettant en oeuvre ses principes : Infection.

Conférence donnée lors du AFUP Day 2020 Lille, ayant eu lieu le 26 juin 2020.

Informations complémentaires

Vidéo

Le speaker

Gaël CRISPYN

Développeur Web à la Caisse d’Épargne Hauts-de-France depuis plus d'un an avec une dizaine d'années de développement PHP à son compteur, Gaël est également un fan de sport (sauf le golf)... Papa d'un petit garçon d'un an, il essaie de jongler entre sa vie de famille et une curieuse envie de parfaire son travail et découvrir les nouveautés autour de ce langage qu'il aime tant. Ah !! Chose très importante, il adore rencontrer des gens et leur parler donc évitez absolument cet homme.

Commentaires

Introduction intéressante au mutation testing, avec une bonne première partie théorique pour contextualiser, placer les soucis des tests unitaires et pouvoir facilement positionner les tests de mutation dans l'écosystème.
Thomas Dutrion, le 26/06/2020

Transcript

[Musique] [Musique] [Applaudissements] Bonjour à tous et à toutes, bienvenue dans cette conférence animée par Gaël, qui nous parlera des tests d'infection.

Pour ceux qui ne l'ont pas déjà vu en conférence, Gaël est développeur PHP depuis 10 ans.

Il est aujourd'hui indépendant, après avoir travaillé entre autres pour la Caisse d'épargne et différentes start-ups.

C'est un habitué des événements de notre antenne, car il a déjà donné plusieurs conférences, notamment à Amiens, et aujourd’hui, il nous présente les tests d'infection. Gaël, c'est à toi.

(Je vérifie que le micro n'est pas coupé).

Bonjour à tous. C'est un vrai plaisir d'être présent aujourd'hui avec vous, Dans le cadre de ce moment de partage, on va parler de qualité de code, lors de cet AFUP Day.

de test et de mutation.

Pour la petite présentation : je m'appelle Gaël, je suis développeur PHP depuis presque dix ans, tout récemment freelance.

J'ai travaillé dans diverses structures : des grosses structures et des start-ups.

Je vous laisse mon mail et mon Twitter, n'hésitez pas à me contacter pour échanger sur ce dont on va parler là, ou d'autres sujets liés à PHP et son écosystème.

Ça me fait toujours plaisir de dialoguer avec des gens.

Pour planter le décor, je pense qu'on est tous d'accord pour dire que notre objectif au quotidien est de rendre le code le plus qualitatif possible Et pour cela, on va passer par différentes étapes.

La première, peut-être la plus importante, c'est l'architecture de notre code.

Faire en sorte de réfléchir à une architecture la plus évolutive, la plus maintenable et qui ait le consensus de tout l'équipe, pour pouvoir délivrer quelque chose le plus en accord avec le besoin exprimé.

La deuxième étape où intervenir, c'est la documentation de notre code.

Elle est hyper importante pour la passation de connaissances entre un senior et un junior ou à des prochaines générations de développeurs.

On va ensuite développer les éléments, les faire valider ; et en parallèle, on va les tester.

On va les tester de plusieurs façons : - fonctionnellement (vérifier que tout un panel de fonctionnalités fonctionne), - pousser les tests un peu plus loin, en utilisation notamment Panther - et à plus bas niveau effectuer des tests unitaires.

Les tests unitaires permettent de valider une petite unité de code.

Ils permettre dans un premier temps de nous rassurer : ils permettent de vérifier qu'une entrée rend bien la sortie attendue.

Pour un code qu'on a écrit nous-même, ça permet de voir qu'on est bien dans les clous, de rassurer les personnes avec lesquelles on travaille.

Quand on partage son code, c'est intéressant de communiquer également un test unitaire, pour vérifier que le code développé s'intègre correctement dans l'écosystème, dans toute l'architecture pensée.

Ça permet de valider notre code au travers des reviews : c'est intéressant de proposer un test unitaire à la personne qui review notre code, qui va pouvoir s'appuyer dessus pour vérifier qu'on a bien tout développé, qu'on a bien pensé à tout ce qu'il fallait.

Ça permet également de documenter notre code, en complément des commentaires, un test unitaire peut être utilisé par un développeur pour comprendre la mécanique d'une fonction et tous les cas possiblement utilisables dans son contexte.

Le problème des tests unitaires c'est qu'ils ont une énorme limite : ils sont écrits par nous ! C'est-à-dire par des développeurs, humains, donc confrontés aux problématiques des humains : être fatigué, ne pas avoir les compétences ou l'expérience suffisante.

On va forcément injecter à un moment donné des problèmes ou des bugs dans nos tests.

Pour essayer de sortir un peu de tout ça et proposer des tests plus qualitatifs, le développeur va avoir besoin de métriques : des graphs, des logs...

Et plus précisément la métrique qui nous vient de suite en tête pour vérifier l'avancement de nos tests unitaires, c'est le code coverage.

Tout le monde à peu près a déjà vu cet écran, il est très simple, c'est le code coverage de PHP Unit.

Précision : tout ce qui va être présenté par la suite le sera avec PHP Unit 9, et donc le coverage proposé par PHP Unit 9.

Si on regarde cet écran que la majorité d'entre vous connait, on voit que le code présent ici a été testé, visiblement couvert de différentes façons : la classe est couverte, les fonctions et les méthodes qui la composent sont couvertes et, également, l'ensemble des lignes qui composent cette fonction.

Plusieurs indices et plusieurs éléments ressortent : la majorité sont des pourcentages, permettant de voir l'avancement, et on voit un petit élément qui s'appelle le CRAP (à peu près au milieu).

Revenons en détail sur ce qu'on a vu : qu'est-ce que le code coverage ? C'est un outil de mesure permettant de mesurer l'avancement de l'écriture nos tests, faire le point sur l'avancement de l'écriture, montrer aux personnes qui travaillent avec nous ce qu'on a fait et ce qui reste à faire.

Si on a une politique d'écriture de test basée sur le code coverage, ça permet de voir temps doit être alloué pour l'écriture de ce qui reste à écrire.

Si on fait un focus sur cette notion de CRAP dont on a parlé avant : le CRAP est un indicateur de risque, au niveau de notre méthode.

C'est le risque de celle-ci à changer : Change Risk Anti-Pattern.

C'est une donnée calculée en fonction de plusieurs indicateurs.

(Je vous met la formule de calcul dessous, trouvée en fonction des recherches).

Le CRAP est calculé ainsi : c'est la complexité cyclomatique de la méthode au carré, multipliée par le pourcentage de lignes non testées dans la méthode, au cube, auquel on rajoute la complexité cyclomatique.

Le pourcentage de lignes non testées dans la méthode, c'est relativement clair.

Mais la complexité cyclomatique de la méthode, c'est quoi ? C'est une unité qui permet de déceler l'ensemble des chemins possibles de la méthode.

Donc plus notre unité de code contient des boucles, des conditions, plus la complexité cyclomatique de la méthode sera élevée.

Si on prend l'exemple précédent, avec une fonction très linéaire, sans conditions, sans boucles, la complexité cyclomatique dans ce cas sera de 1, parce qu'il n'y a qu'un seul chemin possible dans notre méthode.

1 au carré, c'est marrant, ça fait 1 ! On part du principe que le code a été totalement testé, donc le pourcentage de ligne non testées dans cette méthode, c'est 0.

Donc le U(M) présent dans le calcul sera 0.

0 au cube + 1 = 0, que l'on multiplie par 1, ça fait 0 On ajoute à ce résultat la complexité de base qui est de 1, donc le CRAP de notre fonction, si on revient en arrière, il est de 1.

Ce chiffre est la plus petite unité de CRAP que vous pourrez avoir.

il suffit de modifier un seul élément pour que ça augmente de suite.

Si on prend par exemple la même unité de code que l'on a pas testée du tout, le pourcentage de lignes non testées dans la méthode passe à 100%, donc à 1.

Si on reprend la complexité de notre méthode de base, 1 au carré x 1 au cube fait 1 auquel on ajoute 1, qui fait 2.

Donc on a doublé le risque de notre méthode parce que notre test unitaire n'était pas créé.

Voilà, je trouvais intéressant de vous donner un peu de contexte sur les éléments qu'on peut trouver sur le code coverage.

Comme tous les outils, le code coverage a des limites, et pas des moindres.

Si on prend cette classe par exemple, une classe Fibonacci dans laquelle on a une méthode qui calcule la suite de Fibonacci d'un entier.

Cette fonction peut être écrite en une seule ligne, où on prend le cas 0, le cas 1, puis l'ensemble des cas possibles, donc des entiers naturels, jusqu'à la plus haute valeur souhaitée.

Je me suis prêté à l'exercice : j'ai voulu rédiger un test sur cette fonction basique.

J'ai créé une classe de test, j'ai instancié mon objet et j'ai écrit un test permettant de vérifier que la suite de Fibonacci de 0 était bien 0.

J'ai lancé PHP Unit, regardé le code coverage : 100%.

Alors que je n'ai testé qu'un seul cas, il reste le 1 et pléthore de nombres non testés.

Il nous reste au moins deux cas à tester, pour autant le code coverage est de 100%.

Deuxième exemple : une classe avec une fonction dont le comportement diffère en fonction de paramètres d'entrée.

On construit plusieurs sorties différentes.

j'ai donc écrit un test où je passe deux paramètres différents, je l'appelle deux fois et je m'assure que 1=1.

Je pense que mon test passe et qu'il n'y a pas d'erreurs.

Par contre on est conscient que là, absolument rien n'a été testé.

Voilà le code coverage. Donc on n'a écrit aucun test, et pour autant, le coverage qui ressort des tests unitaires dit que tout est couvert.

Troisième exemple : une classe qui contient deux fonctions.

Une fonction getUuid() qui retourne une chaîne unique, et une fonction createArray() qui créé un tableau avec une entrée id qui appelle la fonction précédente.

Je fais le fainéant et j'écris un test qui ne teste que la fonction createArray().

Je donc je m'assure, par rapport au code précédent, que ce qui ressort de ma méthode est un array Test de base : je ne teste que ça.

Pour le coverage, l'ensemble de ma méthode a été testé.

Dans la mesure où le coverage qui apparait est à 100%, pour lui tout est bon.

Que peut-on retenir de tout ça ? Que l'utilisation du code coverage comme indicateur de qualité, c'est pas top ! Mais utiliser le code coverage comme un indicateur de tests non encore faits, peut être intéressant.

Si on reprend les exemples précédents, on a trois cas de figure.

Le code coverage de base, si on reprend la problématique de la suite de Fibonacci, se base sur une couverture linéaire.

Si vous avez un ternaire avec plusieurs cas précis si le code coverage stipule que la ligne a été couverte par votre test, 100% de la ligne a été couverte. C'est ce qu'on appelle le lane coverage.

Le deuxième cas qu'on a vu, on le retrouve très fréquemment dans les entreprises, c'est les tests unitaires qui ne testent rien.

J'en ai vu en repassant derrière des développeurs qui étaient pourtant sûrs d'eux.

C'est le cas vraiment exceptionnel, la caricature, mais parfois, les gens, en utilisant les mocks, ont la sensation que leur code est testé, alors que non.

Dernier cas de figure, c'est ce qu'on appelle le coverage implicite : à partir du moment où vous ne spécifiez aucune entête (on va y revenir après), aucune spécification de l'entête, si une fonction est appelée dans le contexte d'un autre test cette fonction va être considérée comme couverte par le test.

Comment pallier à tout ça ? Comment mesurer efficacement ? La première chose à faire : penser à utiliser l'entête @covers, qui permet d'être beaucoup plus précis sur la couverture de votre code, et d'empêcher ce qui a pu se passer dans notre dernier exemple.

Donc, pensez bien à le mettre au niveau de vos objets et également de vos méthodes.

Le deuxième élément c'est l'utilisation du mutation testing (Coucou Wolverine !).

Le mutation testing est une notion datant des années 70, créee par des Américains à l'université de Yale Comment ça fonctionne, et qu'est-ce que c'est ? Le principe de base du mutation testing, c'est de toujours se baser sur des tests unitaires qui fonctionnent, qui ne pètent pas.

La première étape du processus, c'est de lancer les tests unitaires.

S'ils passent, l'outil de mutation testing va modifier une unité de code existant.

Il va effectuer ce qu'on appelle une mutation et créer un mutant du code source testé.

Et pour chaque mutant créé, il va lancer les tests et regarder ce qu'il se passe.

Si les tests échouent alors le mutant est considéré comme tué, et dans ce cas l'objectif qu'on cherchait à atteindre est OK.

Mais si les tests passent malgré la modification dans le code source, c'est qu'on a un cas non prévu, donc le mutant a survécu, donc l'objectif est KO.

Exemple : une classe de formulaire contenant une méthode qui s'appelle hasErrors() qui compte le nombre d'erreurs du formulaire.

Le retour est tout simple : on compte le nombre d'erreurs et on regarde si ce nombre est supérieur à 0.

Le mutation testing va effectuer une première mutation : remplacer le > par un >=, relancer sa suite et regarder ce qu'il se passe.

Deuxième mutant possible : transformer le > en <.

C'est un exemple, l'univers des possibles des mutants dépend évidemment du contexte du test.

On va avoir des mutants qui pourront aller modifier la visibilité d'une méthode, modifier le retour d'une méthode, modifier arithmétiquement certains éléments clés de votre code et regarder ce qu'il se passe au niveau des tests.

Ça, c'est la théorie, comment est censé se passer le mutation testing.

Concrètement qu'est ce qu'on a aujourd'hui en PHP pour le mettre en place ? En fouillant un peu le web, j'ai trouvé trois frameworks, trois bibliothèques, permettant de faire du mutation testing.

La première qui c'est Humbug, qui n'est aujourd'hui plus maintenue, elle est archivée, je crois que le dernier commit date d'il y a trois ans.

Une seconde bibliothèque, Muta Testing, plus maintenue aujourd'hui, créée par un développeur qui s'appelle Jean-François Lépine.

Et la bibliothèque qui va nous intéresser, qui s'appelle Infection.

C'est un projet open source que l'on peut récupérer via Composer.

Je vous laisse l'adresse du repo Github, si vous avez envie de voir la documentation, qui est assez bien faite, et de l'incorporer dans votre projet.

C'est un projet maintenu par 49 personnes, et relativement correctement suivi.

Infection reprend les principes de base du mutation testing, tels que je vous les ai présentés : lancement des tests unitaires, création des mutants, lancement des suites de tests unitaires pour différents mutants créés.

Il va ressortir plusieurs métriques intéressantes.

Premièrement un mutation score indicateur : MSI, correspondant au total des mutants tués sur le nombre de mutants total qu'il a pu trouver, pour sortir un pourcentage.

Ensuite, il va ressortir le mutation code coverage qui est le nombre de mutants total couvert sur le nombre de mutants total, une notion qui se rapprochera plus généralement des résultats que vous avez pour le code coverage.

Enfin, vous avez le Covered Code MSI, qui est le nombre de mutants tués (je rappelle qu'un mutant est une altération de votre code source) sur le nombre de mutants couverts, multiplié par 100 pour avoir un pourcentage.

Infection contient comme dépendance PHP Parser, puisque sa façon de procéder pour construire et faire ces modifications de code source, se base sur la création d' un abstract syntax tree, pour récupérer toutes les opérandes et variables afin de faire les modifications qu'il juge nécessaires.

Si on prend par exemple cette unité de code, c'est une addition incorporée dans une variable, PHP Parser va construire l'arbre que vous avez dessous, en mettant les variables comme feuilles, et les opérateurs comme noeud ; il va effectuer un parcours général de l'ensemble de l'arbre construit, arbre qui peu être beaucoup plus complexe, à mesure que le code testé va être complexe, et effectuer ce type de modifications, en changeant notamment un nœud en un autre.

De manière générale, ce sont des mutators contenus dans le code source d'Infection, déclenchés dans un contexte précis.

Ces mutators sont des classes, dont voici un exemple : si on a le mutator "minus", chaque classe a une fonction générique canMutate(), qui va en tester pour chaque noeud de l'arbre si le mutator courant peut être déclenché.

Ici on regarde si le noeud courant est l'instance d'un objet PHP Node\Expr\BinaryOp\Minus et s'il peut, il effectue sa mutation.

Dans le cas présent il change en passant sur un noeud de type "plus".

Je vous propose une petite démo afin de vous rendre compte de comment ça fonctionne et mettre en application ce dont on vient de parler.

Je vais vous partager PHP Unit.

J'ai créé un projet pour cette conférence, j'ai récupéré Infection qui s'installe de manière très classique dans Composer, il suffit de d'incorporer la bibliothèque Infection, de spécifier sa version, Composer Install, et vous le récupérez dans votre projet ainsi que le binaire de lancement.

Si on regarde dans l'architecture de la bibliothèque ce qui peut nous intéresser, c'est l'ensemble des mutators possibles.

Tel que je vous l'ai expliqué, on a un certain nombre de mutators sur de l'arithmétique, sur de la division, sur le changement d'un opérateur.

On a également des mutators de type booléens : on va changer la logique en plaçant un inférieur ou égal à la place d'un inférieur, un supérieur ou égal à la place d'un supérieur...

Dans plein de contextes différents, donc vous voyez qu'il y a beaucoup qui peuvent être utilisés chacun dans son propre contexte.

Si on reprend par exemple le cas de notre test implicite : je reprends la fonction que vous aviez en exemple et on reprend le test qu'on a créé, et que je vous ai présenté au préalable.

Infection va se baser sur un fichier de configuration : infection.json.dist qui est l'équivalent du phpunit.xml pour PHP Unit.

Ça c'est vraiment la version basique où on va spécifier la source sur laquelle il va baser sa couverture de test, les logs qu'il va générer et les mutators qu'il va déclencher.

Sachant que par défaut il va utiliser toute sa collection, mais vous pouvez définir un filtrage par défaut en disant "Ce type de mutants, je n'en veux pas".

Par exemple, je ne veux pas qu'on fasse attention à modification de la visibilité de ma méthode.

On verra par la suite que ça peut avoir son importance.

Si on prend les tests : ce test là tel que je vous l'ai présenté.

Je vais lancer Infection.

Pour lancer Infection, la dépendance vous installe un binaire dans vendor/bin que vous pouvez lancer à votre guise.

Pour la démo, je vous montre sur la classe implicite, précisément, mais évidemment si vous ne précisez rien, il va le lancer sur l'intégralité de votre code.

Je lance Infection et on voit qu'il a repris les différentes étapes dont on a parlé précédemment : lancement des tests unitaires ici, et un petit reporting des mutations générées, de ce qu'il a réussi à couvrir et de ce qu'il a réussi à ne pas couvrir.

Là on revoit les trois indicateurs dont je vous ai parlé : un mutation score indicateur à 33%, ce qui correspond bien à un mutant tué sur 3, le mutation code coverage à 100% (c'est grosso-modo l'équivalent du code coverage), le covered code MSI.

A partir de là ce qui est intéressant c'est qu'on va pouvoir commencer à regarder ce qu'il a pu nous générer comme mutations possibles.

Voilà les logs générés : on voit qu'il a réussi à créer deux mutants.

Le premier mutant de notre méthode createArray() il a simplement transformé notre tableau associatif en tableau classique, en virant le "égal" et en testant de manière booléenne la chaîne avec son contenu.

Le deuxième mutant créé, au lieu de retourner mon tableau de base, il a retourné tableau vide.

Remettons nous dans le contexte : il a créé ces deux mutants, il a relancé nos tests, et en changeant totalement le contexte d'utilisation de notre méthode, nos tests ont continué à passer.

Regardons nos tests : on teste ici uniquement si notre sortie est un tableau.

C'est bien joli, c'est un premier jet, mais ce n'est pas suffisant.

On aurait pu également tester, et c'est que les mutants nous indiquent, que notre tableau possède bien la clé id.

Auquel cas, on peut déjà supprimer ce mutant, mais aussi celui-ci puisque du coup on se place dans le contexte d'un tableau associatif.

Effectivement s'il relance la suite de tests avec cette modification, les tests unitaires vont péter.

Pour s'en assurer, je relance ma suite de tests, et effectivement mes trois mutants ont été tués.

Et on passe à un score de mutation beaucoup plus propre et qu'on peut mettre en adéquation avec notre code coverage.

Tout ça a évidemment des limites.

Ça peut être un superbe outil à utiliser au quotidien, pour nous aider à développer des tests, en revanche attention : on a fait nos tests sur un panel relativement restreint de code, sur des toutes petites fonctions qui ne font pas grand-chose Si vous êtes dans le cadre d'un énorme projet, avec beaucoup de fonctions, beaucoup d'objets, il faut jamais oublier que la suite de tests unitaires est relancée pour chaque mutant, donc dès qu'un mutant est créé, on relance la suite de tests, ce qui peut être très lourd.

Plusieurs possibilités : déjà Infection permet via une option de faire du multithreading, ce qui peut nous faire gagner du temps dans l'exécution des tests de mutation.

A côté de ça, un indicateur fait en sorte d'exécuter les tests les plus rapides en premier, puisqu'une fois les tests rapides passés, ça permet d'éliminer un certain nombre de cas, et de se concentrer sur des tests plus complets par la suite.

Il faut aussi faire attention à la notion de mutant équivalent, très importante en mutation testing.

Si on prend le programme juste en dessous, où on initialise un index à 0, et on a une boucle dans laquelle on incrémente cet index jusqu'à 10 et on sort.

Un des mutants possiblement créé par la suite sera de modifier le "égal égal" en "supérieur ou égal".

Mais on se rend compte que ça ne modifie en rien le comportement initial de notre fonction.

Effectivement dès que le mutant est égal à 10 on va sortir, et on en parle plus.

Les mutants équivalents sont assez fréquemment créés dans l'ultilisation du mutation testing, et on peut s'en rendre compte au travers du fichier de logs que je vous ai présenté précédemment.

En conclusion : que peut-on retenir de tout ça ? Déjà que le code coverage n'est jamais une donnée suffisante.

On entend encore trop souvent "j'ai tant de couverture de code, donc ça marche".

Non ! On l'a vu au préalable : es-tu sûr de tes tests ? de la qualité de tes tests ? As-tu vérifié que si tu modifies un élément de ton code, ça change la donne ou pas ? Le mutation testing n'est ni plus ni moins qu'une aide.

A mon sens, c'est un outil à utiliser pendant qu'on créé nos tests, pour s'assurer qu'on est dans les clous, qu'on a rien oublié, mais incorporé dans une CI, je suis pas tellement sûr.

Attention aussi pour les gros projets : ça peut être d'une grande aide, mais qui peut être aussi très chronophage en termes d'exécution.

Donc attention à maîtriser l'outil, à bien setuper la configuration, pour l'inclure dans votre contexte, et à contextualiser dans votre utilisation au quotidien.

Autre chose très importante en conclusion : Infection ne fonctionne pas qu'avec PHP Unit.

Pour Simple PHP Unit, j'ai un peu plus de difficulté, de mémoire, mais avec Atoum ça fonctionne bien et vous pouvez l'utiliser dans plein de contextes différents.

Merci beaucoup pour votre attention, n'hésitez pas si vous avez des questions.

J'espère avoir couvert tous les cas possibles, n'hésitez pas à m'interpeler, me questionner si vous si vous pensez à d'autres choses.

Je vous remercie infiniment.

Merci beaucoup à l'AFUP pour cette édition, que je trouve excellente.

Et je suis disponible si vous avez des questions.

Merci beaucoup.

Merci à toi Gaël, cette conférence était super intéressante.

Je vais reprendre le partage d'écran.

C'était concis et précis, j'ai bien aimé le détail des métriques sur les tests, le détail du calcul que tu as donné, j'ai trouvé ça super intéressant.

On a trois questions, je vais te les lire et tu pourras y répondre.

Première question : est-ce que le mutation testing peut créer des boucles infinies, en modifiant un while par exemple, et provoquer des problèmes ? Des tests et des docs que j'ai pu lire : non.

C'est une excellente question à creuser, pour voir s'il va jusqu'à modifier carrément un comportement, si j'ai bien compris c'était le changer un while, le contenu d'un while.

Pour moi il a une mécanique à terme mais c'est à tester.

Je l'utiliser depuis un certain temps, je n'ai jamais eu le cas.

De mémoire pour avoir déjà vu une question similaire dans un sujet à propos des tests d'Infection, et des mutations, il me semble qu'il est possible de passer une option, une sorte de timeout au niveau des tests d'Infection. A confirmer.

Tu peux passer un timeout mais je ne sais pas s'il a une mécanique interne qui l'empêche de créer un mutant qui peut occasionner une boucle infinie.

Merci pour cette réponse.

Deuxième question : dans quelle mesure le mutation testing pourrait flairer la mauvaise qualité du test montré sur la suite de Fibonacci, qui ne testait que le cas 0 ? J'ai l'impression que le mutation testing ne résous pas ce genre de problèmes ? Alors évidemment il peut pas résoudre tous les cas.

Pour la suite de Fibonacci, je crois qu'il va tester certains opérateurs.

On a le temps de relancer ? On a encore 5 minutes de questions et on peut déborder sur la pause.

Je vais reprendre le partage d'écran, on va relancer la totalité pour voir pour voir ce qu'il peut se passer justement.

Je vais enlever mon filtre, je vais préciser sur Fibonacci.

Donc là il trouve deux mutants qu'il a réussi à tuer et dix cas non couverts.

Si on regarde les logs plus précisément, il modifie plusieurs choses : par exemple le moins en plus, au lieu de mettre - il va mettre -2, +2 également...

Il y a pas mal de choses qui sont faites.

En fait, il teste beaucoup sur du numéraire, ce qui va permettre de voir qu'effectivement, là, malgré toutes ces modifications, comme tu as testé que le case zéro ça continue de passer, sauf que lui derrière, il a effectué beaucoup d'opérations pour checker d'autres cas, en modifiant par exemple ici le triple "égal" en "différent", plusieurs mutants ont été générés, donc on a forcément pas couvert tous les cas.

J'espère avoir répondu à ta question. Merci beaucoup.

Enfin dernière question : est-ce que tu es sûr pour Atoum ? Car il y a une issue ouverte sur leur Github pour l'ingration avec Atoum, qui n'a jamais été closed et je n'ai pas l'impression que ça marche.

D'ailleurs si des personnes veulent aider Jimmy à faire cette intégration.

Je l'avais déjà vu, après il y a peut-être eu d'autres cas où ça n'a pas fonctionné, j'ai questionné les gens, on m'a dit que c'était possible, mais je n'ai pas creusé sur Github.

Donc tu dois peut-être avoir raison.

Parfait, merci beaucoup.

Merci à tous, bonne fin de journée, bon AFUP Day.

[Musique]