Avec la popularisation des méthodes agiles et notamment l’extreme programming (XP), de nouvelles pratiques de développement ont émergé. Les termes TDD, développement itératif et pair programming sont beaucoup utilisés sans pour autant que ces derniers ne soient réellement mis en place au sein des équipes. La pratique qui illustre le mieux cette tendance à mon sens est le TDD.
T’es bien gentil mais le TDD c’est quoi ?
Le TDD ou Test-Driven Development est une méthode de développement de logiciel qui consiste à écrire ses tests avant de coder la fonctionnalité. La question qui doit probablement vous venir à l’esprit doit être « Oui mais que doit-on tester maintenant ?».
C’est bien là toute la subtilité du TDD, on ne teste pas, on spécifie un besoin au travers d’un test qui servira à valider l’implémentation du programme à venir. Le cycle de développement en suivant la méthode TDD est le suivant :
- Écrire un test qui décrit un besoin spécifique
- Vérifier que ce test est KO (c’est normal puisque l’on n’a pas encore codé)
- Écrire le code le plus simple possible pour que le test passe
- Vérifier que le test passe ainsi que les autres tests
- « Refactorer » le code
- Recommencer
Euh d’accord… je comprends le principe mais j’ai du mal à voir l’intérêt.
C’est là tout le problème avec cette méthode de développement. Bien que très efficace une fois maîtrisée, elle nécessite de repenser ses habitudes de développement.
Illustrons cela à travers un exemple concret d’application du TDD :
Nous sommes un prisonnier (un peu fou à priori car nous sommes plusieurs dans sa tête) et nous cherchons à nous évader de prison. Pour que notre projet d’évasion aboutisse, nous ne pouvons pas nous contenter d’aller à la porte d’entrée et de sortir comme si de rien n’était. Nous devons avancer pas à pas de manière itérative :
Test 1 : Puis-je sortir de ma cellule ?
Résultat du test 1 : Non
Résolution simple : Je fabrique un double de la clé
Test 1 : Puis-je sortir de ma cellule ?
Résultat du test : Oui (cool on passe à la suite)
Nouveau test 2 : Puis-je sortir du couloir ?
Résultat du test 2 : Non, il y a un garde qui va sonner l’alerte.
Résolution simple : J’assomme le garde
Test 1 : Puis-je sortir de ma cellule ?
Résultat du test 1 : Oui
Test 2 : Puis-je sortir du couloir ?
Résultat du test 2 : Oui
Phase de re-factoring : Ma clé semble un peu fragile, je la consolide.
Test 3 : Vais-je pouvoir sortir dans la cour sans problème ?
Résultat du test 3 : Non, les autres gardes ne sont pas bêtes, ils vont remarquer un prisonnier qui n’a pas sa place dans la cour sans surveillance.
Solution : Je prends les habits du garde pour me déguiser
Test 1 : Puis-je sortir de ma cellule ?
Résultat du test 1 : La clé bien plus solide est toujours là
Test 2 : Puis-je sortir du couloir ?
Test 2 : Sans ses vêtements, le garde est en train de se réveiller. (Nous avons cassé un test existant avec notre nouvelle fonctionnalité)
Re-factoring : Je l’assomme avec sa propre matraque pour un résultat bien plus efficace.
Test 3 : Vais-je pouvoir sortir dans la cour sans problème ?
Résultat du test 3 : Oui, heureusement que les gardes n’ont pas du tout la mémoire des visages et n’ont aucun soucis à voir un garde qu’ils ne connaissent pas.
Résultat final : Nous avons réussi à sortir de prison.
Ecriture du Test -> Faire en sorte que ce test marche simplement -> Refactoring -> Ecriture d’un nouveau test
Par itération de ce cycle de développement nous avons abouti à la résolution du besoin initial : “sortir de prison” en respectant la pratique TDD.
Ah mais c’est simple, c’est quoi le problème finalement ?
D’une simplicité enfantine sur le papier (ou sur un blog), cette pratique fait émerger en pratique quelques difficultés.
La principale étant de revoir totalement notre façon de coder et de réfléchir. Beaucoup de projets qui essayent de faire du TDD rencontrent après quelques temps des soucis liés à une difficulté d’adaptation, ou de volonté de revoir ses habitudes de développement.
Qui n’a jamais entendu :
« Ouais mais le code est testé, ça revient au même non ? »
Ou encore
« J’ai essayé, mais j’ai du mal à voir l’intérêt, j’ai l’impression de perdre du temps » ?
Le manque de temps, la pression mise sur l’équipe pour développer rapidement de nouvelles features sont des raisons également souvent invoqués pour justifier la décision d’arrêter le TDD.
Tu critiques, tu critiques, mais toi, tu proposes quoi ?
Je ne suis pas magicien et il n’existe pas de solutions miracles pour utiliser le TDD. C’est comme pour les brocolis, il faut goûter avant de dire que l’on n’aime pas !
Un fervent défenseur du TDD pourra très bien vous convaincre de l’intérêt de cette pratique en expliquant que cela permet :
- De produire du code de meilleure qualité, le TDD se basant sur un développement itératif, simple et concis. Il permet de rendre le code plus lisible, maintenable et fonctionnel. De plus la re-factorisation permet de limiter la dette technique. Nous ne nous encombrons plus d’un code figé, qui ne se prête pas aux modifications. Au contraire notre code sera repris, amélioré pour coller au maximum au besoin.
- D’être sûr que notre code est testé. En suivant le TDD, nous sommes sûr d’avoir une couverture de tests suffisante et que le code est fonctionnel.
- D’écrire plus facilement les tests et produire un code plus simple et en général de meilleure qualité. Nos tests n’étant pas dépendants de notre implémentation, nous ne nous retrouvons pas dans une situation où nous devons réfléchir à la meilleure manière de tester notre implémentation. De plus comme nous avons défini un cadre de développement grâce à notre test, nous avons moins de risque de produire du code superflu. Le TDD permet de limiter le développement au strict nécessaire, ce qui cadre avec l’esprit des principes KISS (keep it simple, stupid) et YAGNI (You aren’t gonna need it) [nous reviendrons sur ces principes dans un prochain article].
L’avocat du diable pourrait lui répondre que :
- Le TDD n’empêche pas les bugs. Et croire l’inverse serait une erreur, même si le TDD permet de les limiter, l’écriture du test ne se fait pas en réfléchissant aux cas limites, et ne permet pas de se prévenir à 100% de la détection de bug.
- Un test peut très bien être mal écrit. L’exercice semble simple, mais écrire un “bon” test est plus compliqué qu’il n’y paraît. A quel point peut on faire confiance à nos tests ? Nous pouvons nous retrouver dans une situation où nous remettons en cause notre code avant de remettre en cause le test associé.
- Si le besoin change, on se retrouve avec des tests inutiles. Il n’est pas rare qu’un besoin soit modifié en cours de développement, le TDD est-il utile dans le cas de développements de prototypes où l’on a beaucoup de risques de modifications du besoin ? Est-il cohérent de capitaliser sur les tests qui pourraient devenir obsolètes?
- Quand s’arrêter de tester ? Cette question peut devenir frustrante et chronophage pour un développeur. En effet, dans les méthodes “classiques”, une couverture de code élevée (taux de code exécuté dans les tests unitaires) est souvent l’un des critères utilisés pour définir le moment où les développeurs peuvent “arrêter” d’écrire des tests. Or avec le TDD, il devient difficile de définir un jalon ou un seuil permettant de considérer nos tests comme suffisants.
Pour ma part je trouve le TDD intéressant et j’ai pu constater qu’il apporte des changements bénéfiques au sein d’une équipe. Il oblige les développeurs à comprendre le fonctionnel afin de pouvoir écrire des tests efficaces, et à produire du code plus propre. Mais comme toute pratique, il est nécessaire de bien la comprendre et de savoir si elle peut être compatible avec votre projet ou bien votre équipe.
Il faut garder à l’esprit le gain / le coût de mise en place et de maintenance du TDD dans un projet et au sein d’une équipe. Avant tout, le TDD est une méthode et non une règle absolue qu’il faut appliquer à dans tous les contextes.
Et si vous le testiez plutôt ?
C’est bien j’ai compris mais j’imagine que tu n’es pas la seule personne à avoir parlé du sujet non ?
Loin de là, si vous souhaitez consulter d’autres articles parlant du sujet, je vous conseille :
- https://putaindecode.io/fr/articles/tdd/
- http://igm.univ-mlv.fr/~dr/XPOSE2009/TDD/
- https://thomasbandt.com/tdd-is-a-tool-not-a-religion
Ou encore une intervention à l’Angular Connect de 2018 traitant du TDD par Shai Reznik :
Silvio Vasconcelos