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
Votre changelog reprend les fonctionnalités que vous avez réalisé entre deux versions, ça tombe bien, votre historique git contient ces informations :) On va voir comment les extraire de git pour générer automatiquement un changelog exhaustif et fiable.
Le principe
Pour faciliter la maintenance de votre projet, il est d’usage d’avoir des commits parlants afin que tous les d...
Photo by Yancy Min on Unsplash
Contexte
Je suis développeur au quotidien et j'aime développer en dehors du travail également.
Parfois, il peut m’arriver de faire un kata entre midi et deux par exemple. J'aime bien du coup garder une trace de mon exerc...
On va prendre un cas concret: j’ai fait un commit et en relisant ma merge request, je me rend compte que certains fichiers ne devraient pas y être:
Dans mon cas c’est le fichier src/test/java/com/globaldashboard/unit/dependencies/domain/PomUrlTest.java
...
Dans une application Spring, sauf exceptions, les Beans sont proxifiés. En d’autres mots, on n'interagit pas directement avec eux et ce mécanisme est à la base de l’AOP.
Beaucoup de mécanismes s'appuient sur l’AOP (les Transactions par exemple) et conn...