février
2009
Une bonne pratique qui est apparue très rapidement dans nos dojos de programmation est de bien nommer les tests. En effet, comme les dojos successifs sont séparés de plusieurs semaines, à chaque fois on redécouvre le code, et quand les tests sont mal nommés, nous sommes obligés de les relire entièrement pour les comprendre. En fait l’un des avantages des dojos est de simuler sur une courte période ce qui se passe dans la vie réelle : quand vous revenez sur votre code plusieurs mois ou années plus tard, vous avez parfois l’impression qu’il a été écrit par quelqu’un d’autre, non ?
Par conséquent les participants des dojos sont tous d’accord sur le fait qu’il faut bien nommer les tests. Rien de très surprenant, me direz-vous, c’est une bonne pratique bien connue. Par exemple dans Clean Code, le livre de l’oncle Bob dont j’ai parlé dans un billet précédent, il y a plusieurs points qui parlent du nommage correct. Le deuxième chapitre, écrit par Tim Ottinger, est même entièrement consacré à la question du nommage. Plus loin dans le livre, le chapitre « Tests propres » mentionne notamment :
Qu’est-ce qui rend un test propre ? Trois choses. Lisibilité, lisibilité et lisibilité. La lisibilité est même peut-être plus importante dans les tests unitaires que dans le code de production. Qu’est-ce qui rend les tests lisibles ? La même chose qui rend tout code lisible : clarté, simplicité et densité d’expression. Dans un test vous voulez dire beaucoup avec aussi peu d’expressions que possible. […]
La lisibilité du nom du test est donc importante pour avoir un test « propre ». Enfin dans le chapitre « Odeurs et heuristiques », il y a plusieurs points sur les noms, comme :
N1 : choisissez des noms descriptifs
N’allez pas trop vite pour choisir un nom. Assurez-vous que le nom est descriptif. Rappelez-vous que les significations tendent à dériver au fur et à mesure que le logiciel évolue, donc ré-évaluez fréquemment les noms que vous choisissez.
Ce n’est pas simplement une recommandation pour faire joli. Dans le logiciel les noms constituent 90% de ce qui rend le code lisible. Vous devez prendre le temps de les choisir soigneusement and de les garder pertinents. Les noms sont trop importants pour être traités à la légère.
[…]
N4 : des noms non-ambigus
Choisissez des noms qui rendent non-ambigus le fonctionnement d’une fonction ou d’une variable. […]
Pour l’instant je n’ai pas prêté suffisamment attention à cette question des noms des tests. J’utilisais le premier nom qui me venait à l’esprit. Suite à la « découverte » de cette bonne pratique grâce aux dojos, j’ai donc revisité mon tutoriel sur le développement dirigé par les tests (TDD), et essayé d’améliorer les noms des tests. Le tableau ci-dessous présente les noms de tests du tutoriel, ainsi que le nouveau nom que je leur ai trouvé, parfois avec difficulté.
Pour ce tableau j’ai suivi la convention utilisée par mes collègues, à savoir des groupes de mots séparés par des caractères « souligné », comme dans LeNombreDeSolutionsCalculees_DoitEtreEgal_AuNombreDeSolutionsTheoriques. Mes collègues ne trouvent pas très lisibles d’autres conventions comme tout séparer par des « souligné », comme dans le_nombre_de_solution_calculees_doit_etre_egal_au_nombre_de_solution_theoriques, ou comme tout regrouper et « séparer » par des majuscules, comme dans LeNombreDeSolutionsCalculeesDoitEtreEgalAuNombreDeSolutionsTheoriques.
En ce qui me concerne j’ai une préférence pour le_nombre_de_solution_calculees_doit_etre_egal_au_nombre_de_solution_theoriques, tout simplement parce qu’il est facile d’automatiser l’obtention d’un tel nom : il suffit d’écrire une phrase en français, et une simple macro permet d’obtenir un nom satisfaisant pour un compilateur (voir par exemple Macro to aid BDD test naming style). La troisième convention est également automatisable, mais moins lisible à mon goût.
Pour mieux comprendre les noms ci-dessous, il faut savoir que les 12 pentaminos sont représentés chacun par une lettre qui correspond à sa forme : X, L, V, I etc.
Avant |
Après |
NombreDeSolutions | LeNombreDeSolutionsCalculees_DoitEtreEgal_AuNombreDeSolutionsTheoriques |
EchangeDeDimensions | LeNombreDeSolutions_DoitResterInvariant_ApresEchangeDeDimensions |
TestTotalPentaminos | LaFabrique_DoitRetourner_ToutesLesVariantes |
TestPositionDuPentominoX | LePentaminoX_DoitEtre_CorrectementPositionné_SurUneGrilleDe10x6 |
TestAjoutPentamino | LePentaminoI_DoitPouvoir_EtreAjouteEnPosition1 |
TestAjoutPentaminoSansRepetition | LePentaminoI_NeDoitPasPouvoir_EtreAjouteDeuxFoisEnPosition1 |
TestSolutionTrouveePlateauVide | PourUnPlateauVide_AucuneSolution_NeDoitEtreTrouvee |
PlaceLibreSurPlateauVide | PourUnPlateauVide_LaPosition1_DoitEtreLibre |
PlaceNonLibreXapresI | LaPosition1_NeDoitPlusEtreLibrePourX_ApresAjoutDeI |
PlaceNonLibreEnFinDeLigne | LaPlace_NeDoitPasEtreLibre_EnFinDeLigne_PourI |
PlaceNonLibrePourXen65 | LaPlace_NeDoitPasEtreLibre_EnPosition65_PourX |
PlaceNonLibrePourXen37 | LaPlace_NeDoitPasEtreLibre_EnPosition37_PourX |
PlaceLibrePourXen45 | LaPlace_DoitEtreLibre_EnPosition45_PourX |
ProchainePositionPlateauVide | PourUnPlateauVide_LaProchainePositionLibre_DoitEtre1 |
ProchainePositionPlateauApresI | ApresAjoutDeI_EnPosition1_LaProchainePositionLibre_DoitEtre6 |
DepassementLimites | PourUnPlateauVide_LePentaminoP_PeutEtreAjoute_EnPosition62 |
PlacementVenPosition1 | ApresAjoutDeV_EnPosition1_LesLibertesDePlace_DoiventEtreJustes_EnDiversEndroits |
PlacementVenPosition1viaLignes | ApresAjoutDeV_EnPosition1_LesLignes_DoiventContenir_Respectivement3_1_1_lettresV |
PlacementLenPosition5viaLignes | ApresAjoutDeL_EnPosition5_LesLignes_DoiventContenir_Respectivement1_4_lettresL |
AjouterPuisEnleverI | AjouterPuisEnleverI_DoitEtre_UneOperationInvariante_VerificationParAjout |
PositionLibreApresAjouterPuisEnleverI | AjouterPuisEnleverI_DoitEtre_UneOperationInvariante_VerificationParPositionLibre |
ConstructionSolutionComplete | AjouterDouzePentaminos_DansLeBonOrdre_DoitEtrePossible |
SolutionTrouvee | AjouterDouzePentaminos_DansLeBonOrdre_DoitDonnerUneSolution |
DescriptionLigne1 | PourUneSolutionConnue_LaLigne1_DoitEtreJuste |
DescriptionLigne2 | PourUneSolutionConnue_LaLigne2_DoitEtreJuste |
DescriptionLigne3 | PourUneSolutionConnue_LaLigne3_DoitEtreJuste |
DescriptionLigne4 | PourUneSolutionConnue_LaLigne4_DoitEtreJuste |
DescriptionLigne5 | PourUneSolutionConnue_LaLigne5_DoitEtreJuste |
DescriptionLigne6 | PourUneSolutionConnue_LaLigne6_DoitEtreJuste |
TestPosition27DansPremierQuadrant | LaPosition27_DoitEtre_DansLePremierQuadrant |
TestPosition17DansPremierQuadrant | LaPosition17_DoitEtre_DansLePremierQuadrant |
TestDescription | LaDescriptionDuProgramme_DoitEtre_NonVide |
Obtenir de bons noms a été finalement assez difficile, et consommateur de temps. D’ailleurs je ne suis pas forcément satisfait de tous. Il est probable que travailler en binôme sur la question permettrait d’arriver à de meilleurs résultats.
J’ai constaté aussi que la difficulté de trouver certains noms pouvait indiquer une « mauvaise odeur ». Par exemple pour ApresAjoutDeV_EnPosition1_LesLibertesDePlace_DoiventEtreJustes_EnDiversEndroits, la longueur et le caractère vague du nom m’indiquent que le test fait sans doute trop de choses, et qu’il vaudrait mieux le couper en plusieurs petits tests.
Travailler sur les noms a été également révélateur de défauts potentiels de structuration de mes tests. Ainsi l’une des classes comporte quatre méthodes dont les noms sont construits de manière similaire : PourUnPlateauVide_LePentaminoP_PeutEtreAjoute_EnPosition62, PourUnPlateauVide_LaProchainePositionLibre_DoitEtre1, PourUnPlateauVide_LaPosition1_DoitEtreLibre, PourUnPlateauVide_AucuneSolution_NeDoitEtreTrouvee, alors que les autres noms sont bien différents. Cela indique que ces quatre méthodes devraient certainement aller sur une autre classe de tests, ce qui permettrait de factoriser le contexte « Pour un plateau vide ». Les noms pourraient alors être simplifiés, ce qui augmenterait la lisibilité.
En conclusion, appliquer cette bonne pratique a été un exercice utile pour améliorer mon tutoriel dans une future version, et c’est une pratique que je m’efforcerai d’appliquer au quotidien.
4 Commentaires + Ajouter un commentaire
Commentaires récents
- Des tableaux pour l’intégration d’un équipier dans une équipe Scrum dans
- Rétrospectives, la directive première dans
- Des tableaux pour l’intégration d’un équipier dans une équipe Scrum dans
- Des tableaux pour l’intégration d’un équipier dans une équipe Scrum dans
- Des tableaux pour l’intégration d’un équipier dans une équipe Scrum dans
Merci pour ta réponse. J’avoue que cela me freine un peu pour le moment pour avancer sur le BDD – je n’ai pas le temps de tout regarder/comprendre et j’aimerais bien que quelque chose d’indiscutable émerge (bon de toute manière je n’ai pas encore fait le tour du TDD !).
Bruno
Je crois que c’est un peu le problème du BDD : il y a de nombreux outils et aucun ne semble être indiscutable.
N’en ayant pas une vision exhaustive, je me garderai bien de faire une suggestion, même en me limitant à dotNET…
Bonjour Olivier, merci pour la suggestion. Je suis très intéressé par le BDD, et assez convaincu que c’est l’évolution naturelle du TDD, mais pour l’instant je n’ai pas réussi à bien l’assimiler. Je vais essayer de le pratiquer pour y voir plus clair. Je pense que nous allons y consacrer du temps dans de futurs dojos.
A part l’utilisation de xUnit, est-ce que tu recommandes un outil existant? Je suis un peu perdu entre les nbehave, nspec, MSpec, jpboodhoo_bdd, pour ne parler que de la plateforme .NET…
Bruno
Une question : pourquoi ne pas aller carrément à une structuration de type BDD (contexte->évènement->résultat) pour nommer tous les tests ?
J’ai l’impression que la logique de lecture apporte au moins autant à la compréhension que le nom explicite.
SiLePlateauEstVide_QuandJeChercheUneSolution_JeNenTrouvePas
SiIestEnPosition1_QuandJeChercheUnePlaceLibre_JeLaTrouveEn6
SiLePlateauEstVide_QuandJAjouteUneSuiteConnueDe12Pentaminos_JeNaiPasDEchec
…
J’avais essayé d’écrire un petit billet sur le BDD et son utilisation possible avec un outil xUnit basique : http://agilitateur.azeau.com/post/2008/11/09/Le-Behaviour-Driven-Development-ou-lart-decrire-des-tests-que-tout-le-monde-comprend