Dans cet article, on va voir avec un cas concret ce qu’est le mutation testing et comment ça peut vous être utile au quotidien 😀. Sans plus tarder, prenons l’exemple suivant:
On demande à Bob de créer un programme pour déterminer si un animal est mignon avec les règles suivantes:
Bob fait des implémentations douteuses mais il est consciencieux et fait des tests unitaires après son dev’ 👏
public class CuteServiceTest {
private final CuteService cuteService = new CuteService();
@Test
public void shouldNotBeCuteWithNotEnoughLegs(){
Animal animal = new Animal();
animal.setAnimalType(AnimalType.SNAKE);
animal.setNumberOfLegs(0);
animal.setNumberOfEyes(2);
assertThat(cuteService.isCute(animal)).isFalse();
}
@Test
public void shouldBeCuteWithRightNumberOfLegsAndEyes(){
Animal animal = new Animal();
animal.setAnimalType(AnimalType.CAT);
animal.setNumberOfLegs(4);
animal.setNumberOfEyes(2);
assertThat(cuteService.isCute(animal)).isTrue();
}
}
Bob, run ses tests avec le coverage linéaire et il obtient le résultat suivant:
Bob est satisfait de sa couverture de test et décide que c’est l’exemple parfait pour montrer au stagiaire que les tests unitaires c’est important.
Il retourne dans le code du service, change la valeur de la condition:
et relance ses tests mais catastrophe, les tests sont toujours verts !
Explication 🕵️
Le test shouldBeCuteWithRightNumberOfLegsAndEyes sera vrai peu importe la première condition comme c’est un chat. Le test nous donne un faux sentiment de confiance: On a l’impression de tester la logique de la méthode alors qu’on ne teste qu’une portion de celle-ci.
Revenons au mutation testing
Le mutation testing va automatiser ce que vient de faire Bob manuellement: L’idée est de changer le code et de vérifier que les tests échouent bien quand ils le doivent.
Le mutation testing consiste à modifier votre code (changement de condition, de bornes dans les boucles, de valeurs etc..). On appelle ces modifications des mutants 👾.
Ensuite vos tests unitaires sont joués sur lecode muté.
Si le test est vert, la mutation a survécue. (c’est généralement pas ce qu’on souhaite)
Si le test est rouge, la mutation a été tuée.
Un rapport est ensuite généré et va nous indiquer la pertinence de nos tests unitaires. Plus un test tue de mutation, plus il est robuste.
Voici un exemple de rapport généré par l’outil de mutation testing que j’utilise (PIT Mutation testing)
Ici, on voit dans le rapport que j’ai un test qui n’a pas échoué alors que la condition ligne 8 a été inversée, ce qui peut indiquer que cette portion n’est pas bien testée par nos tests unitaires.
En conclusion
Le mutation testing est un outil très pratique qui va vous permettre de mesurer la robustesse de vos tests unitaires en comparant leur résultat lorsqu’ils s’exécutent sur du code automatiquement modifié.
Le seul inconvénient que je vois au mutation testing est le fait que ça soit un processus qui peut prendre pas mal de temps si on veut l’exécuter sur tout un projet (mais on peut filtrer sur un package ou sur une classe en particulier au besoin).
Sur certains projets, j’ai constaté que des parties complexes étaient mal testées car moins de 20% des mutations générées sur ces parties étaient détectées par les tests unitaires. Il s’est avéré que ces parties n’étaient pas correctement testées donc je trouve cet outil particulièrement intéressant au quotidien !
Notes complémentaires
– Le problème qu’on a simulé en exemple n’aurait pas pu être introduit avec du TDD. La phase “rouge” existe précisément pour éviter de créer un test “vert pour les mauvaises raisons”. Si vous ne connaissez pas le TDD je vous conseille cet excellent article: https://ippon.developpez.com/tutoriels/alm/apprendre-astuces-tdd/
– Le code coverage linéaire est une mesure très limitée, le coverage via tracing/branching est bien plus pertinent car il va indiquer si vous avez couvert les cas vrais et faux d’une condition. Pour voir un exemple: https://www.youtube.com/watch?v=zyM2Ep28ED8&t=328s
On va voir comment avoir en quelques minutes des assertions qui vont vérifier les endpoints de notre API avec des scénarios de ce genre:
Feature: Create a new account
As a visitor,
I can create an account to access the game
Scenario: A visitor creates an account
When I fill the login form with
| email | password |
| [email protected] | Jh0...
Depuis .NET 9, le le support d’OpenAPI est directement inclus dans .NET et ne passe plus par les librairies Swagger par défaut (plus d’info sur ce choix ici si jamais ça vous intéresse).
De façons simplifiée, la librairie Swashbuckle.AspNetCore.Sw...
Description du problème
Par défaut, il n’est pas autorisé de faire des requêtes entre une application qui est dans un domaine A vers une autre qui serait dans un domaine B (pour des raisons de sécurité, il y a plus de détails dans les sources).
S...
On va créer pas-à-pas un raccourci qui nous permettra de générer un IBAN et de l’insérer à l’endroit où se trouve notre curseur. Si vous n’avez pas fait le setup pour développer un plugin Jetbrains, je vous invite à lire notre article sur le sujet.
...