TDD

Test automatisé

Développement des tests unitaires avant le code suivant les phases RED/GREEN/REFACTOR

Test automatisé

Qu'est-ce que le TDD ?

Le développement piloté par les tests (TDD) est une extension du bon vieux test unitaire (UT), l'une des nombreuses stratégies de test du Shift-left. Il consiste principalement à écrire les tests unitaires en premier, conformément au cadre de Kent Beck appelé Extreme Programming (XP) [Beck 2004]. La première étape de cette approche "Test First" (TF) commence par l'échec d'un test automatisé avant de modifier le code. Une fois que le nouvel UT a échoué, le développeur fournit le morceau de code minimal pour que le test passe. Ensuite, une refonte du code peut avoir lieu avant d'ajouter un nouvel UT. Ce cycle est appelé "Rouge/Vert/Refactor" [Beck 2002] :

Le cycle rouge/vert/réfacteur de Kent Beck pour le TDD [Beck 2002].

Ce cycle rouge/vert/réfacteur aide à maintenir la pratique du TDD [Tarlinder 2016].

En général, les développeurs n'aiment pas les tests parce qu'ils les considèrent comme un contrôle visuel factice. UT est :

  • Principalement une activité de développement
  • ET un processus de test qui permet de réaliser des tests de validation et de régression.

Mais faire toutes ces choses en même temps n'est pas suffisant car le développement est une activité contraignante et les développeurs se perdent facilement dans le codage [Beck 2004]. Cela réduit donc l'effort de test, tandis que l'approche TF garantit en quelque sorte la réalisation des tests. 

Dans un contexte de refactoring ou de changement de code, tester en premier est en fait une activité saine pour s'assurer que la régression ne se produira pas. Si les tests ont lieu une fois le changement effectué, les résultats des tests peuvent être altérés par le nouveau code. De plus, le TF permet également d'éviter [Beck 2004] :

  • Ladérive de la portée - si un morceau de code est introduit "juste au cas où", un nouveau test est généré.
  • Couplage - les unités fortement couplées sont difficiles à tester, le fait de tester d'abord permet d'éviter ce problème.
  • Perte de confiance dans le code existant
  • Se perdre dans le codage

En plus du cycle TDD, l'oncle Bob, le pape de l'artisanat du logiciel, fournit les lois "Les 3 lois de la TDD" [Martin 2014] :

  1. Vous n'êtes pas autorisé à écrire du code de production sauf si c'est pour faire passer un test unitaire défaillant.
  2. Vous n'êtes pas autorisé à écrire plus d'un test unitaire qu'il n'est suffisant pour échouer.et les échecs de compilation sont des échecs.
  3. Vous n'êtes pas autorisé à écrire plus de code de production que ce qui est suffisant pour passer le seul test unitaire défaillant.

Pour écrire un UT qui échoue, le développeur peut utiliser l'un des modèles de barre rouge, notamment [Beck 2002] :

  • "One Step Test" : une seule idée qui permettrait d'enseigner quelque chose qui n'est pas évident sur le code.
  • "Starter Test" : un UT qui introduirait un cycle rapide Rouge/Vert/Refactor ; par conséquent, pour comprendre ce qu'il faut tester, un UT simple basé sur des entrées/sorties triviales d'une API est un bon début pour fournir un feedback en quelques minutes.
  • "Tests d'explication" : UTs qui expliqueraient des comportements ou un exemple sur la façon d'utiliser une API - ceci aide à répandre l'utilisation des tests automatisés.
  • "Tests d'apprentissage" : UTs qui expérimentent un nouveau composant tiers en l'utilisant avec des usages de plus en plus délicats du composant.
  • "Un autre test" : Chaque fois que vous avez une idée à côté d'une discussion, il suffit d'ajouter un test à une liste et de revenir à la conversation au lieu de s'éloigner du sujet ; puisque, cela enfreindrait la loi #2, il ne devrait pas être codé.
  • "Test de régression" : le plus petit UT qui échouerait à partir d'un défaut signalé dans un développement piloté par les défauts.

Lorsque les tests unitaires échouent, les modèles dits de la barre verte peuvent être impliqués [Beck 2002] :

  • "Fake It ('Til You Make It)" : retourner simplement la valeur attendue
  • "Mise en œuvre évidente" : il suffit de coder ce qui est évident et d'appliquer une réflexion critique sur "l'évidence" du code requis.
  • "Trianguler" : lorsque l'on n'est pas sûr de l'abstraction correcte du calcul, l'ajout d'une UT supplémentaire pour tester le même code fournit une voie pour transformer progressivement la constante avec les variables et le code et utiliser les UT pour s'assurer qu'il n'y a pas de régression.
  • "One to Many" : lorsqu'il s'agit de gérer une collection d'objets à manipuler, un seul élément sera manipulé et, lorsque cela fonctionne, le code doit être adapté pour le faire fonctionner avec toute la collection (et une liste vide également)

Au moment du refactoring, plusieurs patterns peuvent être impliqués, notamment [Beck 2002] :

  • "Réconcilier les différences" : unifier deux morceaux de code d'apparence similaire.
  • "Isoler le changement" : isoler la partie qui doit changer.
  • "Migrer les données" : dupliquer temporairement les données pour passer de l'ancien au nouveau format et supprimer les anciens formats de données.
  • "Extract Method" : transformer une petite partie d'une méthode en une méthode distincte et appeler la nouvelle méthode.
  • "Méthode en ligne" : déplacer le contenu des méthodes là où elles sont invoquées pour rassembler le code

Il existe d'autres techniques de refactoring partagées par Martin Fowler qui peuvent être examinées [Fowler 2019]. Le refactoring permet d'éviter les odeurs de code et vise à réduire la dette technique [Huumo 2017] de manière régulière, petit à petit, selon la " règle du boy-scout " [Martin 2011]. 

En tant que stratégie de Shift Left, TDD devrait être adopté très rapidement et devrait générer plus de cas de test que de tests d'intégration ou de système pour éviter de construire un cône de glace. Bien qu'efficace, cette technique de test ne fournit qu'une micro-vue sur la solution et doit être complétée par des niveaux de test plus élevés [Beck 2004] et multiplier les types de tests sur la solution dans la boucle de rétroaction [Kohl 2006].

La programmation extrême (XP) nécessite une planification et un retour d'information à plusieurs niveaux et à plusieurs fréquences [DonWells 2013].

La TDD est parfois confondue avec l'ATDD, principalement parce qu'elles se ressemblent, mais aussi parce qu'elles sont structurées de la même manière et qu'elles sont entrelacées dans un cadre à double boucle [Argyris 1977] [Smith 2001] [Ambler 2006].

La double boucle ATDD/TDD [Ambler 2006].

La double boucle déduite de la combinaison ATDD/TDD est bénéfique pour le TDD car elle guide le codage vers les attentes afin d'éviter le syndrome de "se perdre dans le code" et de se concentrer sur les besoins des clients.

Impact du TDD sur la maturité des tests

Bien qu'il soit bien connu, le TDD semble être l'une des pratiques de Software Craftsmanship les plus difficiles, mais si le TDD est correctement compris, c'est le moyen le plus simple d'obtenir une couverture de test élevée et une meilleure qualité de code [Stemmler 2021]. 

Il faut du temps pour apprivoiser le TDD. Avant d'appliquer la TDD au code de production et à la pression temporelle qui l'accompagne, il est extrêmement important de pratiquer un entraînement délibéré, c'est-à-dire de consacrer régulièrement du temps à l'apprentissage par des séances de pratique [Dan North 2012]. Cette formation est un mélange de

  • "Katas", exercices faits et répétés seul, en se concentrant pleinement sur l'objectif pendant plusieurs séances.
  • et les "Dojos", une salle d'entraînement où d'autres pratiquants partagent à travers des katas réalisés en groupe ou en se concentrant sur une connaissance pour étendre les capacités de conception.

Les katas sont généralement de petits problèmes à coder, très faciles ou époustouflants à résoudre grâce à des tests unitaires à réussir. Le problème est exposé et un cadre est mis à disposition (un ensemble de fonctions décrites dans les UT existantes, éventuellement dans un environnement dans lequel le code peut être exécuté et suivi pour être partagé avec une communauté. Il existe des tonnes de katas disponibles sur de nombreux sites web tels que

Lors de la pratique des katas, le but n'est en fait pas de résoudre le problème mais de s'entraîner à une bonne écriture du code, essentiellement en mode Rouge/Vert/Bleu pour commencer à acquérir les 3 lois du TDD, éventuellement avec plus de contraintes au fur et à mesure que l'apprentissage devient des habitudes, puis des réflexes pour adopter n'importe quelle convention de codage même sous contraintes comme la " Calisthénie des objets " [Pissarra 2021] [Moustier 2019-1].

Lors du dojo, des katas peuvent avoir lieu :

  • individuellement avec une rétrospective à la fin de la session
  • dans la programmation en binôme [Williams 2003],
  • en mode Ping Pong [Bulgu 2020] - Dev A code un UT, Dev B le rend vert, le remanie, écrit un nouvel UT et remet le clavier à Dev A, 
  • dans la programmation de Mob
  • ou dans toute autre chose qui impliquerait un partage entre les participants.

Le TDD arrive dans des styles différents :

  • École de Détroit: l'approche TDD classique basée sur une approche de creuser en profondeur [Beck 2002], il s'agit d'une approche ascendante [Henke 2018].
  • École de Londres: une approche basée sur la moquerie[Freeman 2009], il s'agit d'une approche descendante.
  • Test Priority Premise (TPP) de l'oncle Bob: Le TDD se fait par petits pas à travers une série de 12 types de transformation progressive pour introduire de la généricité dans le code [Martin 2013].
  • Test-Commit or Revert (TCR) de Kent Beck: avec le cycle Red/Green/Refactor et les 3 lois du TDD, chaque fois que les UT passent, le code est validé et versionné dans le référentiel de code ; s'ils ne passent pas, le code est révoqué du référentiel [Beck 2018].

À partir de là, il devrait être clair que la TDD nécessite du temps pour passer de l'apprenti au compagnon grâce à certains schémas d'apprentissage [Hoover 2009]. Cependant, TDD est bon pour l'agilité car il démontre ce qui fonctionne [Pettichord 2007] mais UT devrait être FIRST [Ottinger 2009].

  • Rapide: des centaines d'UT doivent être exécutées par seconde
  • Isolats: les origines des problèmes doivent être évidentes
  • Répétable: Les UT doivent être exécutées indépendamment, dans n'importe quel ordre et à n'importe quel moment.
  • Auto-validation: les résultats des UT ne doivent pas nécessiter d'interprétation.
  • En temps opportun: Les UT doivent être rédigées au bon moment, le plus tôt étant le mieux, idéalement avant le code.

Cette qualité FIRST est particulièrement précieuse dans un contexte DevOps, car le pipeline de livraison automatisé peut être lancé des dizaines de fois par jour, éventuellement à chaque livraison de code.

Même si la TDD est difficile, elle présente certains avantages [pulkitagarwal03pulkit 2020] :

  • Vous n'écrivez que le code qui est nécessaire
  • Votre conception est plus modulaire
  • Votre code est plus facile à mainteniret plus facile à remanier.
  • Votre code est documenté par UT
  • Vous déboguez moins
  • Votre couverture de code est plus élevée - bien que ce soit un piège de se fixer comme objectif une couverture de code de 100% [Solnica 2014].

Cependant, la TDD n'est pas une solution miracle, c'est un processus lent, tous les membres de l'équipe doivent adopter l'approche et l'UT doit être maintenue lorsque les spécifications changent [pulkitagarwal03pulkit 2020].

Le TDD a quelques bonnes pratiques pour aider à le maîtriser [Cigniti 2022] :

  • Éviter la complexité fonctionnelle
  • Concentrez-vous sur ce que vous devez réaliser
  • maintenircode d'austérité
  • Testez à plusieurs reprises
  • maintenirintégrité du code
  • Améliorer la connaissance des applications
  • Savoir quand utiliser le TDD - utiliser le simple UT lorsque le risque est faible [Moustier 2019-1], et le TDD lorsque la confiance dans le code est faible.

La TDD impose certains défis [Kohl 2006] :

  • la stratégie de test ne doit pas reposer uniquement sur le TDD et les tests automatisés
  • la stratégie de test ne doit pas reposer sur les maquettes (école de Londres)
  • Le TDD excessif nous éloigne d'une bonne conception
  • maintien de l'UT
  • les suites de tests peuvent devenir encombrantes au fil du temps
  • écrire du code GUI avec TDD est difficile

Le point de vue d'Agilitest sur cette pratique

Les testeurs doivent aider la TDD.

D'un point de vue qui n'est pas en forme de T et que l'on rencontre souvent chez les testeurs fonctionnels purs, les testeurs devraient se demander pourquoi ils devraient s'asseoir en dessous d'un développeur pour jeter un coup d'œil aux UT... Le fait est que... 

  • les tests devraient être effectués le plus tôt possible dans le cycle de vie du développement logiciel, ce qui implique que quelqu'un, pourquoi pas les testeurs, devrait promouvoir cette pratique. TF aborde clairement ce principe de test [ISTQB 2018].
  • Les UT génèrent de la transparence : les testeurs à l'ancienne sont alors en mesure d'évaluer le niveau de qualité d'une version afin de savoir où chercher selon le principe de test " Defect Clustering " [ISTQB 2018].
  • la pratique des promenades Gemba par le champ de base du développeur fournit des tonnes d'informations
  • les techniques d'essai connues des testeurs expérimentés peuvent être partagées avec le développeur afin d'améliorer ses UT et ses compétences en matière d'essai ; en retour, les testeurs connaissent mieux le code ; enfin, cela contribue à l'état d'esprit de la forme en T.

La plupart du temps, les testeurs à l'ancienne s'opposent également à

  • "coder n'est pas mon métier" : ce silo n'est clairement pas un état d'esprit agile, il introduit également des goulots d'étranglement dans le flux de livraison.
  • " Je ne peux pas comprendre le code " : ces testeurs seront capables de faire une revue de code mais ils peuvent demander " Dites-moi ce que fait cet UT ? " pour repérer les failles dans la logique de test ou proposer d'appliquer des techniques de test telles que le " partitionnement par équivalence " [Beizer 1990] [Otsuki 2012] [ISTQB 2015].

Connaître n'est pas maîtriser, cela ne donne peut-être aucune légitimité à l'enseignement de cette pratique mais cela donne la possibilité d'innover et de faire monter en compétence l'équipe de développement.

Pour découvrir l'ensemble des pratiques, cliquez ici.

Pour aller plus loin

© Christophe Moustier - 2021