Test driven developmentTest-Driven Development (TDD), ou le développement piloté par les tests en français[1],[2],[3],[4], est une méthode de développement de logiciel qui consiste à concevoir un logiciel par des itérations successives très courtes (ou petits pas), telles que chaque itération est accomplie en formulant un sous-problème à résoudre sous forme d'un test avant d'écrire le code source correspondant, et où le code est continuellement remanié dans une volonté de simplification. OrigineL'ingénieur logiciel Kent Beck est crédité pour avoir découvert ou redécouvert la méthode. Il a déclaré en 2003 [réf. nécessaire] :
— Kent Beck, Why does Kent Beck refer to the "rediscovery" of test-driven development? What's the history of test-driven development before Kent Beck's rediscovery? Cycles de TDDLes trois lois de TDDÀ l'origine, il s'agissait simplement d'écrire les tests avant de coder, et cette méthode s'appelait le Test-First Design[5]. Puis elle évolua vers une granularité de développement plus fine : pour une ligne de test en échec, une ligne de code de production est écrite, afin que le test réussisse. Afin d'entériner cette granularité de ligne à ligne entre code de test et code de production, trois lois ont émergé.
Ces lois connaissent différentes variantes très similaires entre 2005 et 2008 (2005[7], 2006[8], 2007[9] et 2008[6]), toujours écrites par Robert C. Martin : elles sont ici énoncées selon les termes utilisés en 2008[6]. Or l'emploi de l'expression "test unitaire" dans ces lois prête ici largement à confusion. Par test unitaire, ces lois ne désignent pas un cas de test complet, mais en fait une unique assertion de test. Le cas de test complet devrait être obtenu après avoir répété plusieurs fois les étapes correspondant à ces trois lois, sachant que la résolution d'une erreur de compilation constitue déjà une itération à part entière. En 2014, Robert C. Martin reformule d'ailleurs les lois de TDD[5], et le terme « unitaire » disparaît. Une formulation exacte et exempte de toute ambiguïté de ces 3 lois serait plutôt comme suit. La forme est volontairement affirmative ici, car l'esprit humain comprend plus aisément ce qu'il faut faire que ce qu'il ne faut pas faire.
On fait ainsi la distinction entre l'assertion à l'origine de l'échec du test et le test qui échoue lors de son exécution. De plus, on ne présume pas de la forme de test la plus adéquate, ni ne se restreint à une seule possible, ce qui est d'autant plus pragmatique. Le fait de mettre bout à bout les trois lois de TDD en une seule itération constitue ce qui est appelé un nano-cycle de TDD. À noter que ces trois lois ne couvrent que les conditions à respecter en TDD pour arriver à un test qui réussit, en exprimant le minimalisme attendu du test en échec. Processus cyclique de développementLe processus préconisé par TDD comporte cinq étapes :
Ce processus est répété en plusieurs cycles, jusqu'à résoudre le problème d'origine dans son intégralité. Ces cycles itératifs de développement sont appelés les micro-cycles de TDD. IntérêtVérification et validationLes tests tels qu'ils sont mis à profit en TDD permettent d'explorer et de préciser le besoin, puis de spécifier le comportement souhaité du logiciel en fonction de son utilisation, avant chaque étape de codage. Le logiciel ainsi produit est tout à la fois pensé pour répondre avec justesse au besoin et conçu pour le faire avec une complexité minimale. On obtient donc un logiciel mieux conçu, mieux testé et plus fiable, autrement dit de meilleure qualité. Quand les tests sont écrits après codage, comme c'est le cas traditionnellement[En quoi ?], les choix d'implémentation contraignent l'écriture des tests : les tests sont écrits en fonction du code, et si certaines parties du code ne sont pas testables, elles ne seront pas testées. Au contraire, en testant avant de coder, on utilise le code avant son implémentation, de sorte que les contraintes définies par les tests s’imposent à l'implémentation : le code est écrit en fonction des tests. Le fait d'écrire les tests avant le code en TDD conduit donc à des implémentations testables, c'est-à-dire faciles à tester et entièrement testables. Or la testabilité du code favorise une meilleure conception par un couplage lâche et une cohésion forte, ce qui évite des erreurs de conception courantes. Non-régressionComme chaque test correspond à un changement minimal de code, un test unique permet de faire un lien évident entre une régression et sa cause s'il échoue. Ce lien fait de l'exécution des tests un moment charnière dans un cycle de TDD : on capture l'état instantané du logiciel et on détecte d'éventuelles régressions à la suite du dernier changement. En effet, on veut à tout prix[réf. souhaitée] éviter de modifier le code en présence d'une régression. Dans le cas contraire, on ne saurait dire avec certitude si les tests échouent à cause du dernier changement ou d'un changement antérieur. C'est en cela que les tests déjà écrits constituent un filet de sécurité contre des accidents de parcours où l'on perdrait le lien entre changement et régression. Ce filet de sécurité permet d'envisager avec sérénité n'importe quelle modification du code, qu'il s'agisse d'une transformation (modification qui affecte le comportement) ou d'un remaniement (modification qui ne devrait pas altérer le comportement). Ainsi les nouvelles livraisons des nouvelles versions du logiciel ne devraient pas contenir de régression. L'enjeu du remaniement continu du code est de réaligner la conception du code avec les besoins connus, afin de lutter contre l'entropie logicielle et de prévenir la dette technique. Changer la conception du code sans en altérer le comportement requiert de disposer de suffisamment de tests pour garantir l'absence de régression. Pour cela, un développeur devrait faire appel à différentes formes complémentaires de test, de manière à ne pas les réécrire à chaque remaniement de code, quelle que soit l'étendue des remaniements : tantôt[Quand ?] des tests pour vérifier les résultats exacts d'un traitement, tantôt[Quand ?] des tests pour vérifier que des composants collaborent correctement, sans que ces différents tests échouent ensemble pour les mêmes raisons. Gains recherchésComme les tests sont écrits avant le code, ils trouvent plusieurs utilités en TDD : ils servent d'abord à résoudre — c'est-à-dire à poser — un problème en guidant le codage à chaque étape, ils fournissent ensuite un filet de sécurité contre les régressions et, enfin, ils contribuent à documenter le comportement du logiciel. Grâce à son utilisation des tests, TDD fait gagner en productivité de plusieurs façons.
Programmation en binômeLorsque deux personnes s'associent en binôme pour résoudre un problème de programmation, elles occupent tour à tour deux rôles semblables à ceux d'un équipage de rallye automobile : le pilote, qui tient le clavier, code, tandis que le copilote supervise, prend du recul et guide son pilote par étapes, puis les rôles sont échangés à intervalles réguliers. En appliquant TDD, un binôme peut échanger les rôles de différentes façons : soit entre deux cycles de développement au moment d'itérer, soit entre l'écriture d'un nouveau test en échec et le codage d'un nouveau comportement. La seconde façon d'échanger les rôles force à séparer les préoccupations du test de celles de l'implémentation et met le copilote à contribution pour écrire le test seulement, tandis que la première permet de dérouler un cycle complet en occupant un même rôle. Notes et références
Voir aussiArticles connexes
Bibliographie
Liens externes
|