Réflexions sur TDD. Pourquoi cette méthodologie n'est pas largement reconnue

Bonjour, Habr!

Depuis longtemps et presque sans succès, nous recherchons une tête brillante qui veut évincer M. Kent Beck sur le marché - c'est-à-dire que nous recherchons quelqu'un qui est prêt à écrire un livre sur TDD pour nous. Avec de vrais exemples, une histoire sur vos propres cônes et réalisations. Il y a très peu de livres sur ce sujet, et vous ne contesterez pas les classiques ... c'est peut-être pour cela que nous n'avons pas encore rencontré ce chef.

Par conséquent, nous avons décidé non seulement de nous rappeler à nouveau que nous recherchions une telle personne, mais également de proposer une traduction d'un article plutôt controversé, dont l'auteur, Doug Arcuri, partage ses propres réflexions sur les raisons pour lesquelles le TDD n'est pas devenu courant. Voyons s'il a raison et sinon, pourquoi.



Ce n'est pas une introduction au développement par le biais de tests. Ici, je vais présenter mes propres idées sur le redémarrage de cette discipline et parler des difficultés pratiques des tests unitaires.

Le légendaire programmeur Kent Beck est l'auteur de la méthodologie TDD (développement par test) dans son sens moderne. Kent, avec Erich Gamma, a également contribué à la création de JUnit, un framework de test largement utilisé.

Dans son livre XP Explained (deuxième édition), Kent décrit comment les principes sont formés à l'intersection des valeurs et des pratiques . Si vous construisez une liste de concepts et les remplacez par une sorte de formule, vous obtenez une transformation.

[KISS, Quality, YAGNI, ...] + [Testing, Specs, ...] == [TDD, ...] 

Je respecte profondément ce travail, qui est pour Kent un travail de toute une vie - non seulement pour les chefs-d'œuvre qu'il a créés dans la programmation, mais aussi pour le fait qu'il explore sans relâche l'essence de la confiance , du courage , du don , de la simplicité et de la vulnérabilité . Tous ces attributs étaient indispensables pour l'invention de l'Extreme Programming (XP).

TDD est un principe et une discipline qui sont respectés dans la communauté XP. Cette discipline a déjà 19 ans.

Dans cet article, je partagerai mon opinion sur la façon dont TDD a réussi à s'assimiler. Ensuite, je partagerai des observations personnelles intéressantes qui sont apparues lors de ma session TDD. Enfin, je vais essayer d'expliquer pourquoi le TDD n'a pas tiré aussi fort qu'il n'y paraissait. Allons-y.

TDD, recherche et professionnalisme

Depuis 19 ans, la discipline du TDD fait l'objet de controverses au sein de la communauté de programmation.
La première question qu'un analyste professionnel vous poserait est "quel est le pourcentage de développeurs qui utilisent TDD aujourd'hui?" Si vous interrogiez un ami de Robert Martin (oncle Bob) et un ami de Kent Beck à ce sujet, la réponse serait «100%».

Just Oncle Bob est sûr qu'il est impossible de se considérer comme un professionnel si vous ne pratiquez pas le développement par le biais de tests .

Oncle Bob est étroitement impliqué dans cette discipline depuis plusieurs années, il est donc naturel de lui prêter attention dans cette revue. L'oncle Bob a défendu TDD et a considérablement élargi les limites de cette discipline. Soyez assuré que j'ai le plus grand respect pour l'oncle Bob et son dogmatisme pragmatique.

Cependant, personne ne pose la question suivante: "après tout, pratiquer signifie" utiliser consciemment "- mais cela ne permet pas de juger du pourcentage, n'est-ce pas?" À mon avis, la plupart des programmeurs ne traitaient pas du TDD, même pour une période symbolique.

La réalité est que nous ne connaissons vraiment pas ces chiffres, car personne n'a enquêté activement sur ce pourcentage. Toutes les données spécifiques sont limitées à une petite sélection d'entreprises collectées sur le site Web WeDoTDD . Vous trouverez ici des statistiques sur ces entreprises, des entretiens avec ceux qui pratiquent le TDD tout le temps, mais cette liste n'est pas longue. De plus, il est incomplet, car même une simple recherche révèle d'autres grandes organisations impliquées dans le TDD - mais, peut-être, pas à pleine capacité.

Si nous ne savons pas combien d'entreprises pratiquent le TDD, la question suivante se pose: "Quelle est l'efficacité du TDD, à en juger par ses mérites mesurables"?
Vous serez probablement ravi qu'au fil des années, un certain nombre d'études aient été menées pour confirmer l'efficacité du TDD. Parmi eux, il y a certainement des rapports faisant autorité de Microsoft , IBM , l'Université de Caroline du Nord et l' Université d'Helsinki .



Un diagramme expressif tiré d'un rapport de l'Université d'Helsinki.

Dans une certaine mesure, ces rapports prouvent que la densité d'erreur peut être réduite de 40 à 60%, ce qui nécessite plus de travail; l'exécution augmente de 15 à 35%. Ces chiffres commencent déjà à être retracés dans les livres et les nouvelles méthodologies industrielles, en particulier dans la communauté DevOps.

Répondant en partie à ces questions, nous passons à la dernière: «Sur quoi puis-je compter lorsque je commence à pratiquer le TDD?» C'est pour y répondre que j'ai formulé mes observations personnelles sur TDD. Passons à eux.

1. TDD nécessite une approche verbale

Lorsque nous pratiquons le TDD, nous commençons à rencontrer le phénomène de «désignation de cible». Autrement dit, de courts projets comme la préparation de tests échoués et réussis constituent un sérieux défi intellectuel pour le développeur. Le développeur doit articuler clairement: "Je crois que ce test réussira" et "Je crois que ce test échouera" ou "Je ne suis pas sûr, laissez-moi réfléchir après avoir essayé cette approche."

L'IDE est devenu pour le développeur ce canard en caoutchouc qui supplie de parler activement avec elle. Au minimum, dans les entreprises TDD, les conversations de ce type devraient fusionner en un buzz continu.

Pensez d'abord - puis passez à l'étape suivante (ou aux étapes).

Un tel renforcement joue un rôle clé dans la communication: il vous permet non seulement de prédire votre prochaine étape, mais aussi de vous inciter à écrire le code le plus simple pour vous assurer que le test unitaire passe. Bien sûr, si le développeur est silencieux, il perdra presque certainement son cours, après quoi il devra retourner sur la piste.

2. TDD pompe la mémoire du moteur

Le développeur, se frayant un chemin à travers ses premiers cycles TDD, se fatigue rapidement - après tout, ce processus est gênant et s'arrête constamment. Il s'agit d'une situation courante avec toute activité qu'une personne commence à peine, mais qu'elle n'a pas encore maîtrisée. Le développeur aura recours à des raccourcis, essayant d'optimiser ce cycle afin de se remplir la main et d'améliorer la mémoire du moteur.
La mémoire motrice est indispensable pour que le travail soit amusant et se déroule comme sur des roulettes. En TDD, cela est nécessaire en raison de la répétition des actions.

Obtenez la feuille de triche avec de tels raccourcis. Tirez le meilleur parti de vos raccourcis clavier dans votre IDE pour rendre les boucles efficaces. Alors continuez à chercher.

En quelques séances, le développeur maîtrise parfaitement la sélection des raccourcis, en particulier, quelques séances suffisent pour assembler et exécuter un banc d'essai. Lorsque vous vous entraînerez à créer de nouveaux artefacts, à mettre en surbrillance du texte et à naviguer dans l'EDI, tout cela vous semblera tout à fait naturel. Enfin, vous deviendrez un véritable professionnel et maîtriserez toutes les techniques de refactoring: en particulier, extraire, renommer, générer, élever, reformater et descendre.

3. TDD nécessite au moins un peu de réflexion sur leurs actions à l'avance

Chaque fois qu'un développeur songe à démarrer un TDD, il doit garder à l'esprit une brève carte mentale des tâches à résoudre. Dans l'approche traditionnelle de la programmation, une telle carte n'est pas toujours là, et la tâche elle-même peut être présentée "au niveau macro" ou avoir un caractère de recherche. Peut-être que le développeur ne sait pas comment résoudre le problème, mais n'imagine qu'en gros l'objectif. Les tests unitaires sont négligés vers cet objectif.

Tout en étant assis au travail et en terminant par un autre «asseyez-vous» - essayez également d'en faire un rituel. Pensez et listez d'abord. Jouez avec. Énumérez plus. Ensuite, continuez, réfléchissez. Célébrez. Répétez plusieurs fois. Alors réfléchissez à nouveau et arrêtez.

Soyez catégorique sur le travail. Suivez ce qui a déjà été fait - cochez les cases. Ne pliez jamais jusqu'à ce qu'il y en ait au moins un. Pensez!

Peut-être que le libellé de la liste prendra un certain temps qui ne rentre pas dans le cycle de travail. Cependant, avant de commencer, vous devez avoir une liste. Sans cela, vous ne savez pas où vous allez. Nulle part sans carte.

 //   // "" ->   // "a" ->   // "aa" ->  // "racecar" ->  // "Racecar" ->  //   //    

Le développeur doit répertorier les tests décrits par Kent Beck. La liste de tests vous permet de résoudre le problème sous la forme de cycles qui se passent en douceur les uns dans les autres. Au-dessus de la liste des tests, vous devez constamment traiter et mettre à jour, même si ce n'est que quelques secondes avant les tests. Si la liste de tests est réussie presque complètement moins la dernière étape, alors le résultat est «rouge» et tout le test échoue.

4. TDD dépend de la communication avec les collègues

Une fois la liste ci-dessus terminée, certaines étapes peuvent être bloquées, car elles ne décrivent pas très clairement quoi faire. Le développeur n'a pas compris la liste des tests. L'inverse se produit également - la liste est trop sommaire, dans laquelle il y a beaucoup d'hypothèses sur les exigences qui n'ont pas encore été formulées. Si vous obtenez quelque chose comme ça, arrêtez-vous tout de suite.

Agir sans TDD peut entraîner des implémentations trop complexes. Travailler dans le style de TDD, mais sans réfléchir, sans liste, n'est pas moins dangereux.

Si vous voyez qu'il y a des lacunes dans la liste des tests, levez-vous et dites-le à voix haute.

Dans TDD, le développeur doit comprendre quel produit faire, étant guidé par l'idée des exigences nécessaires dans l'interprétation du propriétaire - et rien de plus. Si l'exigence dans ce contexte n'est pas claire, alors la liste des tests commence à s'effondrer. Cet échec doit être discuté. Une discussion calme aide rapidement à renforcer la confiance et le respect. De plus, c'est ainsi que les boucles de rétroaction se forment rapidement.

5. TDD nécessite une architecture itérative

Dans la première édition de son livre XP, Kent a suggéré que les tests devraient être le moteur de l'architecture. Cependant, au cours de plusieurs années, des histoires sont apparues sur la façon dont les équipes de sprint tombent sur un mur déjà en quelques sprints.

Bien sûr, construire une architecture basée sur des tests est irrationnel. L'oncle Bob lui-même a convenu avec d'autres experts que ce n'était pas bon. Une carte plus complète est nécessaire, mais pas trop loin des listes de tests que vous avez développées «sur le terrain».

Plusieurs années plus tard, Kent a également exprimé cette thèse dans son livre TDD By Example . La compétitivité et la sécurité sont deux domaines principaux où TDD ne peut pas être le moteur, et le développeur doit les traiter séparément. Nous pouvons dire que la compétitivité est un niveau différent de conception de système, la compétitivité doit être développée par itérations, en coordonnant ce processus avec TDD. Cela est particulièrement vrai aujourd'hui, car certaines architectures évoluent vers un paradigme réactif et des extensions réactives (la réactivité est la compétitivité à son zénith).

Construisez une carte plus grande de toute l'organisation. Aider à voir les choses un peu en perspective. Assurez-vous que vous et l'équipe évoluez dans le même cours.

Cependant, l'idée la plus importante est l' organisation de l' ensemble du système et aucune organisation TDD n'est fournie. Le fait est que les tests unitaires sont une chose de bas niveau. L'architecture itérative et l'orchestration TDD sont complexes dans la pratique et nécessitent la confiance entre tous les membres de l'équipe, une programmation en binôme et des revues de code solides. On ne sait pas exactement comment y parvenir, mais bientôt vous pourrez voir que de brèves séances de conception devraient être menées à l'unisson avec la mise en œuvre de listes de tests dans le domaine.

6. TDD révèle la fragilité des tests unitaires et une mise en œuvre dégénérée

Les tests unitaires ont une fonction amusante et TDD la donne complètement. Ils ne permettent pas de prouver l'exactitude. E.V.Dijkstra a travaillé sur ce problème et a discuté de la façon dont la preuve mathématique est possible dans notre cas pour combler cette lacune.

Par exemple, dans l'exemple suivant, tous les tests liés à un palindrome imparfait hypothétique dicté par la logique métier sont résolus. Un exemple est développé en utilisant la méthodologie TDD.

 //    @Test fun `Given "", then it does not validate`() { "".validate().shouldBeFalse() } @Test fun `Given "a", then it does not validate`() { "a".validate().shouldBeFalse() } @Test fun `Given "aa", then it validates`() { "aa".validate().shouldBeTrue() } @Test fun `Given "abba", then it validates`() { "abba".validate().shouldBeTrue() } @Test fun `Given "racecar", then it validates`() { "racecar".validate().shouldBeTrue() } @Test fun `Given "Racecar", then it validates`() { "Racecar".validate().shouldBeTrue() } 

En effet, il y a des défauts dans ces tests. Les tests unitaires sont fragiles même dans les cas les plus triviaux. Il n'a jamais été possible de prouver leur exactitude, car si nous essayions, cela nécessiterait un travail mental incroyable, et l'apport requis pour cela serait impossible à imaginer.

 //   ,      fun String.validate() = if (isEmpty() || length == 1) false else toLowerCase() == toLowerCase().reversed() //   ,    fun String.validate() = length > 1 length > 1 

length > 1 peut être appelée une implémentation dégénérée . C'est tout à fait suffisant pour résoudre la tâche, mais elle-même ne rapporte rien sur le problème que nous essayons de résoudre.

La question est - quand un développeur doit-il arrêter d'écrire des tests? La réponse semble simple: quand c'est suffisant du point de vue de la logique métier , et non selon l'auteur du code. Cela peut nuire à notre passion pour le design et la simplicité peut énerver les gens . Ces sentiments sont compensés par la satisfaction à la vue de notre propre code propre et la compréhension que par la suite le code peut être refactorisé en toute confiance. Tout le code sera très soigné.

Notez que pour tout le manque de fiabilité, des tests unitaires sont nécessaires. Comprenez leurs forces et leurs faiblesses. Si l'image complète ne correspond pas, peut-être que cet écart aidera à remplir les tests de mutation .

TDD a ses avantages, mais cette méthodologie peut nous distraire de la construction de châteaux de sable inutiles. Oui, c'est une limitation , mais grâce à elle, vous pouvez vous déplacer plus rapidement, plus loin et de manière plus fiable. C'est peut-être ce que l'oncle Bob avait à l'esprit en décrivant ce que, de son point de vue, cela signifie être un professionnel .

Mais! Peu importe la fragilité des tests unitaires, ils sont absolument indispensables. Ce sont eux qui transforment la peur en courage . Les tests fournissent une refactorisation douce du code; en outre, ils peuvent servir de guide et de documentation pour tout nouveau développeur qui peut immédiatement se mettre sur la bonne voie et travailler au profit du projet - si ce projet est bien couvert par des tests unitaires.

7. TDD montre la boucle inverse des instructions de test

Faites un pas de plus. Pour comprendre les deux phénomènes suivants, nous étudions d'étranges événements récurrents. Pour commencer, jetons un coup d'œil à FizzBuzz. Voici notre liste de tests.

 //    9  15. [OK] //  ,  3,  Fizz  . // ... 

Nous avons fait quelques pas en avant. Maintenant, notre test échoue.

 @Test fun `Given numbers, replace those divisible by 3 with "Fizz"`() { val machine = FizzBuzz() assertEquals(machine.print(), "?") } class FizzBuzz { fun print(): String { var output = "" for (i in 9..15) { output += if (i % 3 == 0) { "Fizz " } else "${i} " } return output.trim() } } Expected <Fizz 10 11 Fizz 13 14 Fizz>, actual <?>. 

Naturellement, si nous assertEquals données d'assertion attendues dans assertEquals , le résultat souhaité est obtenu et le test est effectué.

Parfois, les tests échoués donnent le résultat correct nécessaire pour réussir le test. Je ne sais pas comment nommer de tels événements ... peut-être des tests de vaudou . Le nombre de fois où vous voyez cela - cela dépend en partie de votre paresse et de votre étiquette lors des tests, mais j'ai souvent remarqué de telles choses lorsqu'une personne essaie d'obtenir une implémentation qui fonctionne normalement avec des ensembles de données prêts à l'emploi et prévisibles.

8. TDD montre la séquence des transformations

TDD peut vous piéger. Il arrive que le développeur soit confus dans les transformations faites par lui-même, qu'il utilise pour réaliser l'implémentation souhaitée. À un moment donné, le code de test se transforme en goulot d'étranglement où nous décrochons.

Une impasse se forme. Le développeur doit prendre du recul et désarmer, supprimant certains des tests afin de sortir de ce piège. Le développeur reste non protégé.

L'oncle Bob lui-même était susceptible de tomber dans de telles impasses au cours des années de sa carrière, après quoi il a apparemment réalisé que, pour réussir le test, vous devez définir la séquence d'actions correcte afin de minimiser la probabilité de tomber dans une impasse. De plus, il devait réaliser une autre condition. Plus les tests deviennent spécifiques, plus le code est généralisé .



La séquence des transformations. Vous devez toujours vous efforcer pour l'option la plus simple (en haut de la liste).

Il s'agit de la condition de priorité de transition . Apparemment, il existe un certain ordre de risque de refactoring, que nous sommes prêts à atteindre en passant le test. Habituellement, il est préférable de choisir l'option de conversion indiquée tout en haut de la liste (la plus simple) - dans ce cas, la probabilité d'entrer dans une impasse reste minime.

Le TPP ou l'analyse des tests de l'oncle Bob , pour ainsi dire, est l'un des phénomènes les plus intrigants, technologiques et passionnants actuellement observés.

Utilisez-le pour garder votre code aussi simple que possible.
Imprimez la liste TPP et placez-la sur votre bureau. Vérifiez avec lui pour éviter de vous retrouver dans une impasse. Faites-en une règle: l'ordre doit être simple.

Ceci conclut l'histoire de mes principales observations. Cependant, dans la dernière partie de l'article, je voudrais revenir à la question à laquelle nous avons oublié de répondre au début: "Quel est le pourcentage de programmeurs professionnels utilisant TDD aujourd'hui?" Je répondrais: "Je pense qu'il y en a peu." Je voudrais enquêter sur cette question ci-dessous et essayer d'expliquer pourquoi.

Le TDD est-il bien ancré dans la pratique?

Malheureusement non. Subjectivement, il semble que le pourcentage de ses supporters soit faible, et je continue de chercher des données. Mon expérience en recrutement, en leadership d'équipe et en développement personnel (ce qui me fascine) me permet de faire les constats suivants.

Raison 1: Manque de contact avec la vraie culture des tests

Je peux raisonnablement supposer que la plupart des développeurs n'ont pas eu l'occasion d'apprendre et de travailler dans une véritable culture de test .

La culture de test est un environnement dans lequel les développeurs pratiquent et améliorent consciemment l'art du test. Ils forment constamment des collègues qui n'ont pas encore suffisamment d'expérience dans ce domaine. Une rétroaction a été établie dans chaque paire et chaque demande de pool qui aide tous les participants à développer des compétences de test. En outre, il existe un soutien sérieux et un sens du coude dans toute la hiérarchie des ingénieurs. Tous les managers comprennent l'essence du test et y croient. Lorsque les délais commencent à expirer, la discipline de test n'est pas abandonnée, mais elle continue d'être suivie.

Ceux qui ont eu la chance de se tester dans une telle culture de test, comme par exemple, j'ai eu la chance de faire de telles observations. .

2:

TDD, , xUnit Patterns Effective Unit Testing . , -, , , . .

. , . . , , , … .

3:

: , , . , , ; - , – .

4:

, , TDD . , .

, , : « , ». : , « » — .

– .

Conclusion

XP – , . – , . TDD.

, , . «» , , – , .



XP Explained. , , .

, - .

, – , . , , , .

, , , .

TDD « » , . TDD . TDD .

. TDD , . TDD — , , . , TDD , . , .

 @Test fun `Given software, when we build, then we expect tests`() { build(software) shoudHave tests } 

, TDD – , , . . , , , .

Source: https://habr.com/ru/post/fr427853/


All Articles