[FR] TDD: Les 3 choses que tu peux tester

EvywebEvyweb
6 min read

Quand tu commences à faire du TDD, c’est normal d’être un peu paumé.
Tu ne sais pas trop quoi tester, tu avances à tâtons, et si en plus tu découvres le framework de test en même temps parce que t’en as jamais fait… c’est le combo parfait pour galérer pendant plusieurs heures.

Mais y’a des petits trucs simples à retenir qui peuvent t'aider. Ça va pas régler tous tes soucis, certes, mais ça peut clairement te débloquer quand tu ne sais pas trop par où commencer.

Parce qu’au fond, si tu prends deux secondes pour y penser, à part quelques cas très particuliers (tests de perf, de sécu, etc.), y’a que trois manières de vérifier qu’un bout de code fait ce que tu attends de lui.

Pas dix, pas cinquante. Juste trois.

Et une fois que tu as compris ça, tout devient beaucoup plus simple.

💡
Dans cet article, on ne parlera que d’output.

La seule question à te poser

Commence juste par répondre à cette simple question:

Est-ce que le résultat que je veux vérifier est direct, indirect ou une collaboration?

Bon là je vois déjà ta tête:

“Oula c’est quoi ces trucs, je comprends rien, laisse tomber…”.

→ T’inquiètes pas on va voir ça ensemble avec des exemples.

Direct output

On commence avec le plus simple. Et ça tombe bien parce que c’est celui que tu vas essayer d’utiliser le plus souvent. C’est aussi celui qui va te paraitre le plus naturel.

Imagine que le code que tu essaie d’écrire est celui d'une “machine”. On ne sait pas ce qu’il y a à l’intérieur.

Cette machine, tu lui as donné un but. Ici son but est de “créer une potion à partir d’une herbe”.

→ Donc ce que la machine va “recevoir en entrée”, ce sont des herbes.

→ Ce qu’elle “renverra en sortie” c’est une potion.

Si tu traduis ça en test, vu qu’on est en train de faire du TDD, tu pourrais imaginer quelque chose comme ça (avec des strings pour rester simple):

it('should create a green potion from green herb', () => {
    // Arrange
    const machine = new Machine();
    const herb = 'green-herb';

    // Act
    const potion = machine.createPotion(herb);

    // Assert
    expect(potion).toBe('green-potion');
});

Tu vois, on retranscrit assez facilement ce qu’on a dit juste au-dessus. On passe une herb en entrée, et en retour on reçoit une potion verte. Le résultat est direct.

C’est le cas le plus simple à tester car tu vas pouvoir passer des valeurs différentes en entrée et checker le résultat en sortie assez facilement.

C’est souvent le cas des calculs d'ailleurs.

Quand on calcule la somme de deux nombres, c’est assez naturel de se dire “si je passe 1 et 2 en entrée, le result doit être égal à 3”.

it('should make the sum of two numbers', () => {
    // Arrange
    const a = 1;
    const b = 2;

    // Act
    const result = sum(a, b);

    // Assert
    expect(result).toBe(3);
});
💡
Parfois, t’as même rien à passer en entrée ou tu n’as pas la main sur tes entrées aussi facilement, je sais. Mais on verra ça dans un autre article, ici on se focus uniquement sur l’output.

Bref, le truc c’est de se dire que, si le code que tu souhaites tester renvoie quelque chose, c’est du “direct output” et c'est facilement testable.

Indirect output

Parfois ça peut être un peu plus compliqué. Le résultat peut être disponible mais “ailleurs”. Et donc pour écrire ton test, tu vas devoir prendre en compte cette contrainte toi aussi. Reprenons notre exemple avec une petite subtilité.

Notre machine est reliée à une lampe qui devient verte quand la potion a bien été créée.

Dans notre cas, la lampe est externe à la machine. La machine y accède d’une façon ou d’une autre, et déclenche un changement d’état.

Comment elle fait? On s’en fiche, tout ce qu’on sait c’est que la machine ne “renvoie pas” une lampe en sortie… C’est donc un “indirect output”.

Si tu traduits ça en test, une solution possible serait:

it('should change the color of the lamp to green', () => {
    // Arrange
    const lamp = new Lamp();
    const machine = new Machine(lamp);
    const herb = 'green';

    // Act
    machine.createPotion(herb);

    // Assert
    expect(lamp.color).toBe('green');
});

On lit facilement que la machine a accès à la lamp, et qu’après avoir créé la potion, la lamp devient verte.

Mais contrairement à la version direct output, ça demande une petite gymnastique mentale en plus.

Et voilà pour notre deuxième façon de tester.
Encore une dernière, et on a fait le tour.

Collaborateurs

Suivant les tests que tu souhaites faire, parfois tu vas vouloir vérifier que les choses se déroulent exactement comme tu le souhaites.

Ce ne sera pas un résultat qui sera checké, mais un comportement.
On va vouloir vérifier que les acteurs (qu’on va appeler collaborateurs) remplissent bien leur rôle comme il faut. Voici un exemple.

La lampe doit clignotter 3 fois pour avertir qu’une nouvelle potion est prête.

Plusieurs choses ici.

Allumer/éteindre une lampe, c’est peut-être long. Ça consomme de l’énergie, ça use l’ampoule.
Bref, c’est pas un truc qu’on veut faire pour de vrai en test.
Le mieux serait d’utiliser un “test double” ou “doublure de test” en francais pour éviter ces problèmes.

Ce qu’on veut ici, c’est s’assurer que la méthode blink() a bien été appelée 3 fois.

it('should make the lamp blink 3 times', () => {
    // Arrange
    const lamp = new Lamp();
    spyOn(lamp, 'blink').mockImplementation();    
    const machine = new Machine(lamp);
    const herb = 'green';

    // Act
    machine.createPotion(herb);

    // Assert
    expect(lamp.blink).toBeCalledTimes(3);
});

✋ Hophophop! T’as plein de manières de faire des doublures de tests.
Ici je retranscrits uniquement ce que nous avons dit au dessus.
J’ai utilisé le spyOn avec mockImplementation (dispo dans jest & vitest) dans cet exemple car je pense que tu as déjà dû les rencontrer dans la doc ou des tutos.
Mais ce n’est clairement pas la seule facon de faire.
On verra notamment comment créer nos propres doublures sans librairie et les différences entre chaque type de doublure (dummy, stubs, spy, fakes etc…). Mais en attendant, on va rester sur cette proposition.

Ici, tu ne vérifies pas un état mais plutôt que ton collaborateur a bien été sollicité comme prévu pour remplir son job.
→ Tu valides donc un comportement.

Ce que tu dois retenir

Quand tu veux écrire un test, demande toi si tu veux vérifier:

  • Ce que le code produit (direct)

  • Ce qu'il change ailleurs (indirect)

  • Ce qu'il fait faire aux autres (collaboration)

Pour aller plus loin

Bien entendu les termes ici ont été simplifiés. Mais ça devrait t'aider à y voir plus clair pour écrire tes prochains tests.

Si tu souhaites pousser plus loin, tu peux retrouver tout ça sous les noms:

  • DirectState Verification

  • IndirectObservable Side Effect, Indirect Output

  • CollaborationInteraction Testing, Behavior Verification

→ Mais t’inquiètes pas, on verra tout ça un peu plus dans les prochains articles.

Voilà quelques livres incontournables sur le TDD (liens d’affiliation Amazon):

Dans un prochain article, on parlera des inputs directs et indirects donc reste à l'écoute.

0
Subscribe to my newsletter

Read articles from Evyweb directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Evyweb
Evyweb