Depuis quelques années, je code en Test Driven Development (enfin, je pense). Cette approche, si simple à expliquer, est pourtant très compliquée à comprendre et à maîtriser !
Quand on parle de TDD, on décrit trois étapes :
- On code un test rouge (qui ne compile pas ou qui ne passe pas) ;
- On fait passer ce test de la manière la plus directe possible ;
- On retravaille le code pour avoir une implémentation de meilleure qualité.
On décrit ce cycle en trois étapes :
Cette introduction est tout à fait correcte pour expliquer la méthode ! Malheureusement, avec ces explications seules cette approche est souvent mal comprise. Je vous partage ici ma compréhension de cette manière de coder diablement efficace.
Ce que TDD n'est pas !
Commençons par enfoncer une porte ouverte : TDD n'est pas une méthodologie de tests ! C'est le nom de la méthode et pourtant il m'a fallu des années pour vraiment le comprendre. Bien sûr, en codant en TDD notre code sera testé mais ce n'est qu'un heureux effet de bord, ce n'est absolument pas l'objectif de cette approche.
TDD n'est pas, non plus, test-first. En TDD vos tests sont là pour guider votre implémentation (à tout moment). En test-first vos tests sont là pour valider, au final, que votre implémentation fait ce que vous pensez qu'elle doit faire.
Autre chose que TDD n'est pas : chronophage. TDD me fait gagner beaucoup de temps au quotidien. Malheureusement, je ne connais pas d'étude sérieuse cherchant à évaluer les impacts de TDD sur la vélocité, ce ne sont que des observations personnelles.
Enfin, TDD n'est absolument pas simple. Le nom de la méthode tient en 3 mots, son cycle en 3 étapes (Red, Green, Refactor) et pourtant, son apprentissage demande un important investissement personnel. Il faut souvent des mois de pratique pour retrouver notre vélocité passée.
Mais alors c'est quoi TDD ?
Dans mon utilisation actuelle, TDD est une méthode de design de code. Même s'il m'aide parfois à formaliser des algorithmes, je compte surtout sur cette approche pour faire émerger des designs simples et pertinents.
Dans la grande majorité des cas, TDD a remplacé avantageusement les designs sur tableau blanc que j'ai pourtant faits pendant des années. En étant ancré à la fois dans la réalité du code et du métier, TDD permet d'obtenir plus rapidement des designs de meilleure qualité.
En TDD, je peux aussi explorer des pistes à moindre coût. Même s'il me faut 5 itérations pour me rendre compte qu'un design est mauvais, je n'aurais perdu qu'une dizaine de minutes et j'aurais une bien meilleure compréhension de la problématique à résoudre.
Avec les tests obtenus par effet de bord et l'entraînement permanent au refactoring, je peux très facilement adapter mon code aux changements et faire vivre la solution en suivant les évolutions du métier.
Je vois donc TDD comme une approche de design pérenne : à court terme, je gagne du temps et un meilleur design, et, à moyen terme, je fais vivre ma base de code sans risque.
Comment faire au quotidien ?
Commençons par une autre évidence : une personne qui ne fait pas de code ne peut pas vous dire comment faire du code ! De fait, un PO ou un chef de projet ne peuvent pas vous dire de faire du TDD ou de ne pas en faire. Enfin... ils peuvent, mais ils ne devraient pas, simplement parce qu'ils ne peuvent pas prendre la mesure de la difficulté que cela représente.
Si un·e pair (un·e autre développeur·euse) vous demande de faire du TDD, iel doit le faire avec votre accord et en vous accompagnant. Personne ne peut vous imposer un apprentissage aussi exigeant mais, à l'inverse, personne ne peut vous empêcher d'essayer ! C'est votre manière de travailler. Exception, cependant dans le cas du travail en pair ou en mob : il faudra sûrement faire du TDD quoi qu'il arrive.
Si votre management insiste lourdement et vous micromanage pour que vous ne fassiez pas de TDD, franchement, il y a d'autres missions et d'autres entreprises qui cherchent des développeur·euse·s motivé·e·s...
Une fois votre décision prise, il faudra vous lancer dans l'éprouvant apprentissage de TDD. Cet apprentissage commencera probablement par des prérequis :
- Une montée en compétence sur les langages et frameworks que vous utilisez au quotidien ;
- Faire du code testable (souvent SOLID avec de l'IOC) ;
- Comprendre et connaître les outils de tests ;
- Faire du test first (les tests puis le code) avant de faire du TDD ;
- Apprendre à détecter les code smells et connaître leurs refactorings ;
- Une base de code vous permettant d'apprendre ! Une fois maîtrisé, vous pourrez faire du TDD sur du legacy mais pour apprendre ce n'est pas idéal ;
- ...
Petit à petit, en faisant preuve de discipline, votre design émergera de vos tests, et non plus l'inverse ! Vos tests seront alors bien meilleurs, plus pertinents, plus stables, liés au métier et non plus à des détails d'implémentation. Vous allez commencer à gagner du temps en plus de gagner en qualité mais ce ne sera certainement pas dans la première semaine, il faudra être patient !
Est-ce que je vais dans la bonne direction ?
Quand on apprend TDD, on se demande parfois si on va dans la bonne direction. J'aurais vraiment aimé qu'on me dise de garder en tête que TDD n'est pas une méthode de tests, que c'est une méthode de design !
Nos tests doivent être écrits pour guider notre design. Notre premier test va donc valider le cas le plus simple à résoudre : bien souvent ce sera un cas d'erreur, un cas aux bornes ou un cas trivial. Notre premier test de cas nominal ne se fera que lorsqu'on aura obtenu une signature satisfaisante en validant rapidement le comportement dans des cas "absurdes".
Autre point essentiel : on n'écrit jamais plus de code que nécessaire tout en ne cherchant pas à se piéger soi-même. On ne cherche pas à tester tous les cas, on veut simplement du code qui marche : "I get paid for code that works, not for tests" - Kent Beck. TDD n'est pas là pour valider tous les cas possibles, le but est d'avoir un code qui réponde de manière évidente au besoin.
En écrivant uniquement le code nécessaire : celui demandé par un test, on suit naturellement le principe YAGNI (You Ain't Gonna Need It) et c'est une bonne chose ! De fait, il ne faut pas essayer de répondre tout de suite aux besoins qui arriveront peut-être dans un mois ou plus. Si un doute subsiste pour une feature alors il n'y a pas de doute et il faut clarifier le point avant de faire le code.
Chercher à faire émerger un design, laisser les tests me guider, ne pas essayer de tester tous les cas (mais quand même tester tout mon code) et ne faire que le code nécessaire sont parmi les conseils que j'aurais aimé avoir quand je me suis lancé dans ce changement de manière de travailler.
J'aurais aimé qu'on me dise que ça en valait la peine, que mon quotidien n'en serait que plus plaisant, enfin, ça je n'en suis pas certain... Je ne pense pas que j'aurais pu croire quelqu'un me disant ça. C'est vrai, comment une inversion de mon schéma de pensée remettant le besoin métier au centre de mes préoccupations pourrait avoir un impact positif sur mes réalisations ?