Un premier pas dans le Développement Dirigé par les Tests

Ou comment apprendre le TDD

Vous entendez souvent parler de TDD mais n’avez jamais osé demander ce que c’était ? Vos collègues se marrent autour de la fontaine à eau parce qu’ils n’arrivent pas à faire échouer un test ? Vous ne comprenez pas pourquoi ils tiennent absolument à avoir un test qui échoue ? Votre voisin rouscaille parce qu’il subit des régressions alors que tous les tests de l’application sont au vert ? Cet article est fait pour vous.

C’est quoi, le TDD ?

#La théorie

Le TDD, ou DDT en bon françois (Développement Dirigé par les Tests), est une pratique de développement dans laquelle aucun code applicatif n’est produit avant qu’un test en échec ne soit écrit. Pas n’importe quel test, ne me faîtes pas dire ce que je n’ai pas dit. Un test qui met en évidence un bug, ou une fonctionnalité manquante.

Le pratiquant du TDD, lorsqu’il a besoin d’écrire du code, passera forcément par la case “écriture d’un test unitaire qui échoue”. Lorsqu’il a ce cas de test qui reproduit le bug ou le manque fonctionnel, et qu’il constate que le test en question est en échec, il peut alors commencer à réaliser le code applicatif pour faire en sorte que le test passe. Et uniquement le code nécessaire à ça. Pas plus. Non, pas une ligne de plus.

Lorsqu’il a écrit le minimum de code utile à faire réussir son test, il peut passer par une étape de refactoring (ou remaniement en bon françois). Il retravaille son code pour faire en sorte qu’il soit le plus propre possible, toujours en s’assurant que le test qu’il a écrit continue de réussir.

A quoi ça sert, le TDD ?

Le TDD présente quatre gros avantages.

Il assure d’avoir une couverture de tests aussi complète que nécessaire (tout bout de code applicatif sera surveillé par un test). De ce fait, les régressions sont sous bonne garde, et toute évolution sera donc bien plus confortable à effectuer. Un développeur n’aura plus la crainte de mettre à genou la moitié de l’application en corrigeant un malheureux bug dans un sous-module de l’application. On assure ici une sécurité quant aux évolutions et corrections de l’application.

Le TDD évite également l’anti-patron du tas de code, ou encore le phénomène d’“étouffement du bug sous un amoncèlement de code”. Cela évite d’écrire une dizaines de lignes, alors que seule la dernière d’entre elles corrige effectivement le problème.

Cette pratique a également le mérite de simplifier le code produit. Etant donné que le test est écrit en premier, le développeur s’efforce d’écrire une API claire et facile à utiliser. Le test est écrit de telle façon que la classe à tester est conçue pour être simple à utiliser. Le développeur écrit son code de test de la façon qu’il souhaite voir la classe fonctionner, au lieu d’avoir à tordre le code de test pour répondre à la difficulté d’utiliser une classe mal dessinée.

Enfin, le TDD sert de mode d’emploi. Le code de test d’une classe est simple, ce qui permet à un utilisateur ultérieur de la classe de comprendre de quelle façon il faut l’instancier et l’utiliser.

Ca a l’air chouette, ton truc, mais comment ça marche ?

Sur le papier, c’est très simple. Le TDD est un cycle en trois étapes.

Cycle du **TDD**

Première étape, vous écrivez un test (ou un ensemble de tests) qui met(tent) en évidence le problème à corriger. Vous les exécutez et vérifiez que les tests sont bien tous rouges. Si ce n’est pas le cas, cela signifie qu’une partie de ce que vous alliez corriger est déjà correct. En pratique, cela peut arriver si l’implémentation que vous allez écrire est une nouvelle classe (nous le verrons dans le TP en fin de cet article).

Seconde étape, vous écrivez le code applicatif censé corriger le problème, et ne lâchez pas le morceau jusqu’à ce que tous les tests passent au vert.

Dernière étape, vous repassez sur votre code pour le nettoyer au maximum, le réachitecturer, le remanier, etc. Bref, il faut que ça brille. A la fin de ce remaniement, il faut que tous les tests passent encore.

Alors, c’est simple ?

La théorie est bien jolie, mais j’ai du mal à voir en pratique.

Réfractaire ? Vous allez rentrer dans le rang, et fissa ! Pour ce faire, je vous propose une séance de travaux pratiques. Vous allez expérimenter cette pratique en révisant vos classiques, à savoir les expressions régulières. Pas de panique ! Je vous donnerai un petit coup de pouce.

#La pratique

##Installation

Je vous invite à vraiment mettre en pratique. Voir, c’est bien. Faire, c’est mieux. Alors allons-y.

En préambule, il vous faudra installer Maven. Si ce n’est pas déjà fait, je vous invite à l’installer et à le configurer en suivant les directives sur le site d’Apache.

Ensuite, clonez le dépôt Github pour récupérer le projet du TP dans le répertoire de votre choix (il vous faudra installer Git si ce n’est pas déjà fait !) :

git clone https://github.com/remidoolaeghe/TP-TDD_RegExp.git

Entrez ensuite dans le dossier cloné

cd TP-TDD_RegExp

Retournez ensuite au commit marquant le début du TP. Pour les impatients, il sera toujours possible de sauter d’un commit à un autre pour brûler les étapes ou avoir la correction d’une étape.

git checkout c17c508

Vous voilà donc avec un projet dans l’état initial pour bien débuter votre TP. Pour les plus paresseux, une ligne de commande pour exécuter Maven et un éditeur de texte suffiront. Pour les plus courageux, sortez votre meilleur IDE et importez-y le projet. Dans cet exemple, ce sera eclipse.

Allez dans le menu File -> Import… Puis importez le en tant que projet maven

Import du projet maven

Cliquez sur Browse… et sélectionnez le dossier dans lequel vous avez cloné le projet depuis github.

Import du projet maven

Cliquez ensuite sur Finish, et vous voilà avec un projet dans votre IDE.

Le projet est configuré pour fonctionner sous Java 8. Si votre IDE est un peu ancien, il vous faudra faire un peu de gymnatique supplémentaire, car votre eclipse vous dira que le projet n’est pas dans la bonne version de Java. Il vous faudra alors spécifier à eclipse un JRE 8 dans le menu Preferences puis Installed JRE, et ensuite de spécifier dans le projet que le JRE à utiliser est le JRE 8.

Premier contact

Maintenant que votre projet est prêt à être utilisé, prenons-en connaissance.

Etat du projet

Nous avons une interface RegExpPlayer, son implémentation RegExpPlayerImpl et sa classe de test RegExpPlayerTest, contenant un test qui ne fait rien. Nous considèrerons que cela représente la couverture de test du projet dans son état actuel. Exécutez une commande Maven install sur votre projet, pour constater que tous les tests sont au vert. Pour ce faire, clic droit sur votre projet -> Run As -> Maven Install.

Build du projet

Rouge (red)

Nous voulons maintenant implémenter une classe de vérification de chemins Windows et UNIX. Ce sera le rôle de RegExpPlayer et son implémentation. Nous écrirons une méthode vérifiant qu’un chemin (sous forme d’une String) passé en paramètre est un chemin valide pour Windows, et une autre méthode pour UNIX.

public boolean validateUNIXPath(String path);
	
public boolean validateWindowsPath(String path);

La première étape consiste à écrire des tests qui échouent. Pour ce faire, nous allons nous faciliter la vie en écrivant d’abord l’API, avec les deux méthodes indiquées ci-dessus. Ensuite, nous laisserons le soin à Eclipse de générer automatiquement les implémentations dans la classe RegExpPlayerImpl.

Tu disais qu’on écrivait d’abord le test avant le code applicatif !

En théorie, oui. En pratique, on aime définir les API avant pour des questions de code qui compile. Votre IDE sera plus gentil avec vous si votre code est correct syntaxiquement.

Maintenant que votre code compile, il vous faut écrire vos cas de tests. Comment faire ? Réfléchissez aux cas nominaux, c’est-à-dire aux cas pour lesquels votre vérification de chemin doit dire “ok”. Pensez également à une liste suffisamment exhaustive de cas d’erreur, pour vérifier que votre implémentation refuse les cas d’erreurs.

Si vous n’êtes pas famillier avec JUnit, voici comme écrire un test, dans la classe de test RegExpPlayerTest:

@Test
public void testSimple(){
	Assert.assertTrue(true == true);
}

Spoiler ! Si vous êtes en manque d’inspiration, voici une (courte) liste de chemins à tester, sur Windows et UNIX :

C:\Users
C:\Users\Me
/Users
/Users/Me
[chemin vide]

Attention à bien échapper vos antislashes en les doublant, dans votre code. Le compilateur ne les comprendra pas sinon.

Une fois vos tests écrits, lancez vos tests. Une bonne partie devrait être en échec. Ceux passant au vert sont probablement dûs à l’implémentation automatique retournant false. Ce sont donc de faux positifs.

Pour les plus flemmards d’entre vous, il est possible de récupérer la correction en effectuant un saut sur le commit qui va bien :

git checkout ef2fe34

Vert (green)

Maintenant que vos tests sont prêts, vous pouvez commencer à réaliser l’implémentation de chacunes des deux méthodes. Si les expressions régulières ne vous sont pas familières, reportez-vous à ce tuto. Ne cherchez pas à être très complets. Pour le principe, vous pouvez vous contenter de chemin contenant uniquement des caractères non accentués.

Utilisez pour ce faire les classes du JDK java.util.regex.Matcher et java.util.regex.Pattern

Allez-y !

Pour les plus pressés, la correction peut être récupérée ainsi :

git checkout 683f5b0

Remaniement (refactor)

Vos implémentations sont faites, et vos tests sont au vert ? Il est maintenant temps de jeter un coup d’oeil aux points d’amélioration. N’y a-t-il pas moyen de factoriser certaines lignes de code entre vos deux méthodes de la classe RegExpPlayer ? Si oui, allez-y, et faites tourner vos tests jusqu’à ce que tous soient verts.

La correction finale est disponible ici :

git checkout 19aa75a

Conclusion

Excellent, vous venez de réaliser un utilitaire de vérification de chemins multi-plateforme en faisant appel au TDD ! Ce n’était pas bien compliqué au final.

Alors pourquoi ne pas appliquer ce principe à vos travaux au quotidien ? Les gains sont nombreux, et le coût d’utilisation est risibile.

Vous voulez continuer de vous entraîner ? Pourquoi ne pas ajouter des tests pour vérifier des chemins avec des caractères accentués ? Ecrivez les tests, vérifiez qu’ils sont en échec. Ecrivez l’implémentation, et voyez les tests passer au vert. Enfin, remaniez votre code pour qu’il reste toujours propre !

Photo de Rémi Doolaeghe

Rémi Doolaeghe est un développeur freelance Java partisan du manifeste agile et de l'artisanat logiciel. Il a développé son expérience pendant 5 années au service d'éditeurs de logiciels de la métropole lilloise avant de devenir développeur indépendant. L'agilité et la collaboration sont ses moteurs dans un univers où l'expertise technique à elle seule n'est qu'un premier pas vers l'excellence.

Partagez cet article :