Une gestion d’erreur de son API .Net automatique via ses exceptions
...
Sommaire
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 l’implémentation suivante:
public class CuteService { public boolean isCute(Animal animal){ boolean isCute; if(animal.getNumberOfEyes() == 2 && animal.getNumberOfLegs() == 4){ isCute = true; }else { isCute = false; } if(animal.getAnimalType().equals(AnimalType.CAT)){ return true; } return isCute; } }
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:
if(animal.getNumberOfEyes() == 2 && animal.getNumberOfLegs() == 4)
Par:
if(animal.getNumberOfEyes() == 1999999 && animal.getNumberOfLegs() == 4)
et relance ses tests mais catastrophe, les tests sont toujours verts !
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.
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 le code 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.
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 !
– 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
PIT Mutation Testing https://pitest.org/quickstart/maven/
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 c...
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...