Tous les exemples que j'ai lus et vus sur les vidéos de formation ont des exemples simplistes. Mais ce que je ne vois pas si je fais le "vrai" code une fois que je suis vert. Est-ce la partie "Refactor"?
Si j’ai un objet assez complexe avec une méthode complexe, j’écris mon test et le strict minimum pour le faire passer (après sa première défaillance, le rouge). Quand dois-je revenir en arrière et écrire le code réel? Et combien de code dois-je écrire avant de refaire le test? Je devine que le dernier est plus d'intuition.
Edit: Merci à tous ceux qui ont répondu. Toutes vos réponses m'ont énormément aidé. Il semble y avoir différentes idées sur ce que je demandais ou sur quoi je m'embarrassais, peut-être qu'il y en avait un, mais ce que je demandais, c'était: disons que j'ai une application pour la construction d'une école.
Dans ma conception, je veux commencer par une architecture, User Stories, etc. À partir de là, je prends ces user stories et je crée un test pour tester la user story. L'utilisateur dit, nous avons des gens qui s'inscrivent à l'école et paient des frais d'inscription. Alors, je pense à un moyen de faire échouer cela. Ce faisant, je conçois un test de classe pour la classe X (peut-être étudiant), qui échouera. Je crée ensuite la classe "Student". Peut-être que "l'école" je ne sais pas.
Quoi qu’il en soit, la conception de TD me force à réfléchir à l’histoire. Si je peux faire échouer un test, je sais pourquoi il échoue, mais cela suppose que je puisse le faire passer. Il s'agit de la conception.
Je compare cela à la réflexion sur la récursivité. La récursivité n'est pas un concept difficile. Il est peut-être plus difficile d’en garder une trace dans votre tête, mais en réalité, le plus difficile est de savoir, quand la récursivité "se déclenche", quand arrêter (à mon avis, bien sûr). Je dois donc penser à ce qui cesse la récursivité en premier. Ce n'est qu'une analogie imparfaite, et elle suppose que chaque itération récursive est un "pass". Encore une fois, juste un avis.
Dans la mise en œuvre, l'école est plus difficile à voir. Les grands livres numériques et bancaires sont "faciles" dans le sens où vous pouvez utiliser une arithmétique simple. Je peux voir a + b et retourner 0, etc. Dans le cas d'un système de personnes, je dois réfléchir plus sérieusement à la façon de mettre en œuvre cela. J'ai le concept de l'échec, passe, refactor (principalement en raison de l'étude et de cette question.)
Ce que je ne sais pas est basé sur le manque d'expérience, à mon avis. Je ne sais pas comment échouer en inscrivant un nouvel étudiant. Je ne sais pas comment faire échouer une personne en tapant un nom de famille et que celui-ci soit enregistré dans une base de données. Je sais comment faire un + 1 pour les mathématiques simples, mais avec des entités comme une personne, je ne sais pas si je ne fais que tester pour voir si je récupère un ID unique de base de données ou quelque chose d'autre lorsque quelqu'un entre un nom dans un base de données ou les deux ou ni l'un ni l'autre.
Ou peut-être que cela montre que je suis toujours confus.
Réponses:
Vous ne "retournez" pas et écrivez du "code réel". C'est tout le code réel. Ce que vous faites est de revenir en arrière et d’ajouter un autre test qui vous oblige à modifier votre code afin de faire passer le nouveau test.
Quant à combien de code écrivez-vous avant de refaire le test? Aucun. Vous écrivez zéro code sans échec du test qui vous oblige à écrire plus de code.
Remarquez le motif?
Passons en revue (un autre) exemple simple dans l'espoir que cela aide.
Facile peazy.
Pas ce que vous appelleriez du vrai code, non? Ajoutons un test qui force un changement.
Nous pourrions faire quelque chose d'idiot
if n == 1
, mais nous allons passer à la solution sensée.Cool. Cela fonctionnera pour tous les numéros non FizzBuzz. Quelle est la prochaine entrée qui forcera le code de production à changer?
Et encore. Ecrivez un test qui ne passe pas encore.
Et nous avons maintenant couvert tous les multiples de trois (qui ne sont pas également des multiples de cinq, nous le noterons et reviendrons).
Nous n'avons pas encore écrit de test pour "Buzz", alors écrivons ça.
Et encore une fois, nous savons qu'il y a un autre cas que nous devons traiter.
Et maintenant, nous pouvons gérer tous les multiples de 5 qui ne sont pas également des multiples de 3.
Jusqu'ici, nous avions ignoré l'étape de la refactorisation, mais je vois des doublons. Nettoyons cela maintenant.
Cool. Maintenant, nous avons supprimé la duplication et créé une fonction bien nommée. Quel est le prochain test que nous pouvons écrire qui nous obligera à changer le code? Eh bien, nous évitons le cas où le nombre est divisible par 3 et 5. Écrivons-le maintenant.
Les tests réussissent, mais nous avons plus de duplication. Nous avons des options, mais je vais appliquer "Extract Local Variable" plusieurs fois afin que nous procédions au refactoring au lieu de la réécriture.
Et nous avons couvert chaque entrée raisonnable, mais qu'en est-il des entrées déraisonnables ? Que se passe-t-il si nous passons 0 ou un négatif? Écrivez ces cas de test.
Cela commence-t-il à ressembler à du "code réel"? Plus important encore, à quel moment a-t-il cessé d'être un "code irréel" et de devenir "réel"? C'est quelque chose à méditer sur ...
J'ai donc pu le faire simplement en recherchant un test qui, je le savais, ne réussirait pas à chaque étape, mais je me suis beaucoup entraîné. Quand je suis au travail, les choses ne sont pas toujours aussi simples et je ne sais pas toujours quel test va forcer un changement. Parfois, j'écris un test et je suis surpris de voir qu'il passe déjà! Je vous recommande vivement de prendre l’habitude de créer une "liste de tests" avant de commencer. Cette liste de test doit contenir toutes les entrées "intéressantes" auxquelles vous pouvez penser. Vous ne les utiliserez peut-être pas tous et vous ajouterez probablement des cas au fur et à mesure, mais cette liste sert de feuille de route. Ma liste de tests pour FizzBuzz ressemblerait à ceci.
la source
Le "vrai" code est le code que vous écrivez pour réussir votre test. Vraiment . C'est si simple.
Lorsque les gens parlent d'écrire le strict minimum pour rendre le test vert, cela signifie simplement que votre vrai code doit suivre le principe YAGNI .
L’idée de l’étape de la refactorisation est simplement de nettoyer ce que vous avez écrit une fois que vous êtes satisfait qu’il répond aux exigences.
Tant que les tests que vous écrivez englobent réellement les exigences de votre produit, une fois qu'ils sont satisfaisants, le code est complet. Pensez-y, si toutes vos exigences professionnelles ont un test et que tous ces tests sont verts, que reste-t-il à écrire? (D'accord, dans la vie réelle, nous n'avons pas tendance à avoir une couverture complète des tests, mais la théorie est valable.)
la source
switch
cas avec un cas pour chaque test unitaire qui réussirait tous les tests et échouerait pour toute autre entrée.switch
" était conçu comme un extrême logique consistant à "écrire le strict minimum pour rendre les tests écologiques". Pour moi, la question du PO est la suivante: où est le principe qui évite ce gros problème dans TDDswitch
?La réponse courte est que le "code réel" est le code qui fait passer le test. Si vous pouvez réussir votre test avec autre chose que du code réel, ajoutez d'autres tests!
Je conviens que de nombreux tutoriels sur le TDD sont simplistes. Cela fonctionne contre eux. Un test trop simple pour une méthode qui, par exemple, calcule 3 + 8 n'a vraiment pas d'autre choix que de calculer également 3 + 8 et de comparer le résultat. Cela donne l’impression que vous allez simplement dupliquer le code, et que les tests représentent un travail inutile inutile, source d’erreurs.
Lorsque vous êtes bon en test, cela vous aidera à structurer votre application et à écrire votre code. Si vous ne parvenez pas à faire des tests utiles et judicieux, vous devriez probablement repenser un peu votre conception. Un système bien conçu est facile à tester - ce qui signifie que les tests sensibles sont faciles à concevoir et à mettre en œuvre.
Lorsque vous écrivez vos tests d’abord, observez leur échec, puis écrivez le code qui les fait passer. C’est une discipline qui garantit que tout votre code dispose des tests correspondants. Je ne suis pas esclave de cette règle quand je code; souvent j'écris des tests après coup. Mais faire les premiers tests aide à rester honnête. Avec une certaine expérience, vous remarquerez que vous vous codez dans un coin, même lorsque vous n’écrivez pas d’abord des tests.
la source
assertEqual(plus(3,8), 11)
, nonassertEqual(plus(3,8), my_test_implementation_of_addition(3,8))
. Pour les cas plus complexes, vous recherchez toujours un moyen de prouver que le résultat est correct, autre que le calcul dynamique du résultat correct dans le test et la vérification de l'égalité.plus(3,8)
a retourné le résultat correct en soustrayant 3 de celle - ci, en soustrayant 8 de cela, et de vérifier le résultat contre 0. Il en est ainsi de toute évidence équivalente àassertEqual(plus(3,8), 3+8)
à un C'est un peu absurde, mais si le code testé crée quelque chose de plus compliqué qu'un simple nombre entier, prendre le résultat et vérifier l'exactitude de chaque partie est souvent la bonne approche. Alternativement, quelque chose commefor (i=0, j=10; i < 10; ++i, ++j) assertEqual(plus(i, 10), j)
plus()
peut ajouter 10 à des choses. Nous nous basons toujours sur les valeurs de boucle initiales vérifiées par le programmeur, bien sûr.Parfois, certains exemples sur le TDD peuvent être trompeurs. Comme d'autres personnes l'ont déjà fait remarquer, le code que vous écrivez pour réussir les tests est le code réel.
Mais ne pensez pas que le vrai code apparaît comme par magie - c'est faux. Vous avez besoin d'une meilleure compréhension de ce que vous voulez atteindre, puis de sélectionner le test en conséquence, en partant des cas les plus faciles et des cas critiques.
Par exemple, si vous devez écrire un lexer, vous commencez par une chaîne vide, puis par une série d'espaces, puis par un nombre, puis par un nombre entouré d'espaces, puis par un nombre incorrect, etc. Ces petites transformations vous mèneront à Le bon algorithme, mais vous ne sautez pas du cas le plus facile à un cas très complexe choisi bêtement pour obtenir le code réel.
Bob Martin l'explique parfaitement ici .
la source
La partie refactor est nettoyée lorsque vous êtes fatigué et que vous voulez rentrer chez vous.
Lorsque vous êtes sur le point d'ajouter une fonctionnalité, la partie refactor est ce que vous modifiez avant le prochain test. Vous refactorisez le code pour faire de la place pour la nouvelle fonctionnalité. Vous faites cela lorsque vous savez quelle sera cette nouvelle fonctionnalité. Pas quand vous l'imaginez.
Cela peut être aussi simple que de renommer
GreetImpl
àGreetWorld
avant de créer uneGreetMom
classe (après l' ajout d' un test) pour ajouter une fonctionnalité qui imprimera « Salut maman ».la source
Mais le vrai code apparaîtrait dans la phase de refactorisation de la phase TDD. C'est-à-dire le code qui devrait faire partie de la version finale.
Les tests doivent être exécutés chaque fois que vous effectuez un changement.
La devise du cycle de vie du TDD serait: REFACTOR RED GREEN
RED : Écris les tests
VERT : Faites une tentative honnête pour obtenir du code fonctionnel qui passe les tests le plus rapidement possible: dupliquer le code, nommer de manière obscure des variables de hacks du plus haut ordre, etc.
REFACTOR : Nettoyez le code, nommez correctement les variables. Séchez le code.
la source
La phase rouge correspond à l’ écriture du code.
Dans la phase de refactoring , l’objectif principal est de supprimer du code.
Dans la phase rouge , vous faites tout pour que le test réussisse le plus rapidement possible et à tout prix . Vous ignorez totalement ce que vous avez déjà entendu parler de bonnes pratiques de codage ou de modèle de conception. Rendre le test vert est tout ce qui compte.
Lors de la phase de refactoring , vous nettoyez le désordre que vous venez de créer. À présent, vous devez d’abord vérifier si le changement que vous venez d’apporter figure en haut de la liste dans la liste Priorité de transformation et s’il existe une duplication de code, vous pouvez le supprimer en appliquant probablement un modèle de conception.
Enfin, vous améliorez la lisibilité en renommant les identifiants et en extrayant des nombres magiques et / ou des chaînes littérales en constantes.
Merci d'avoir signalé cela.
C'est donc la phase verte où vous écrivez le vrai code
Dans la phase rouge , vous écrivez la spécification de l' exécutable ...
la source
Vous écrivez Real Code tout le temps.
A chaque étape, vous écrivez du code pour satisfaire les conditions que votre code satisfera pour les futurs appelants de votre code (que vous soyez ou non ...).
Vous pensez que vous n'écrivez pas de code utile ( réel ), car dans un moment vous pourriez le reformuler.
Cela signifie que même si vous modifiez le code, les conditions de satisfaction du code restent inchangées. Et les contrôles ( tests ) que vous avez mis en place pour vérifier votre code sont déjà là pour vérifier si vos modifications ont changé quoi que ce soit. Donc, le code que vous avez écrit tout le temps est là, mais d'une manière différente.
Une autre raison pour laquelle vous pensez peut-être que ce n'est pas du vrai code, c'est que vous donnez des exemples dans lesquels le programme final peut déjà être prévu par vous. Ceci est très bon, car il montre Vous avez des connaissances sur le domaine Vous programmez en.
Mais plusieurs fois les programmeurs sont dans un domaine qui est nouveau , inconnu pour eux. Ils ne savent pas quel sera le résultat final et TDD est une technique pour écrire des programmes étape par étape, documentant nos connaissances sur le fonctionnement de ce système et vérifiant que notre code fonctionne de cette manière.
Lorsque j'ai lu The Book (*) sur TDD, pour moi, la caractéristique la plus importante qui ressortait était la liste: TODO. Cela m'a montré que le TDD est également une technique permettant aux développeurs de se concentrer sur une chose à la fois. Donc, ceci est également une réponse à votre question à propos de Combien de code réel à écrire ? Je dirais assez de code pour se concentrer sur une chose à la fois.
(*) "Développement piloté par les tests: par exemple" de Kent Beck
la source
Vous n'écrivez pas de code pour faire échouer vos tests.
Vous écrivez vos tests pour définir à quoi devrait ressembler le succès, ce qui devrait tous initialement échouer car vous n'avez pas encore écrit le code qui va réussir.
L’écriture de tests qui échouent initialement a pour objectif essentiel de faire deux choses:
Le point derrière le refactor rouge-vert est que l'écriture des tests corrects vous donne la confiance de savoir que le code que vous avez écrit pour réussir les tests est correcte et vous permet de refactoriser avec la certitude que vos tests vous informeront dès que quelque chose se brise, vous pouvez donc immédiatement le réparer.
Selon ma propre expérience (C # / .NET), pur-test-first est un idéal inatteignable, car vous ne pouvez pas compiler un appel à une méthode qui n'existe pas encore. Donc, "test first" consiste vraiment à coder les interfaces et les implémentations de stub en premier, puis à écrire des tests sur les stubs (qui échoueront initialement) jusqu'à ce que les stubs soient correctement étoffés. Je ne suis jamais en train d'écrire "code défaillant", juste en me basant sur des bouts.
la source
Je pense que vous pouvez être confus entre les tests unitaires et les tests d'intégration. Je crois qu'il peut également y avoir des tests d'acceptation, mais cela dépend de votre processus.
Une fois que vous avez testé toutes les petites "unités", vous les testez toutes assemblées ou "intégrées". C'est généralement un programme complet ou une bibliothèque.
Dans le code que j'ai écrit, l'intégration teste une bibliothèque avec divers programmes de test lisant des données et les transmettant à la bibliothèque, puis vérifiant les résultats. Ensuite, je le fais avec des fils. Ensuite, je le fais avec des fils et un fork () au milieu. Ensuite, je l'exécute et tue -9 après 2 secondes, puis je le lance et vérifie son mode de récupération. Je le fuzz. Je le torture de toutes sortes de façons.
Tout cela est AUSSI un test, mais je n'ai pas un joli affichage rouge / vert pour les résultats. Soit ça réussit, soit je fouille quelques milliers de lignes de code d'erreur pour comprendre pourquoi.
C'est là que vous testez le "code réel".
Et je viens de penser à cela, mais peut-être que vous ne savez pas quand vous êtes censé avoir fini d'écrire des tests unitaires. Vous avez terminé d'écrire des tests unitaires lorsque vos tests exercent tout ce que vous avez spécifié. Parfois, vous pouvez perdre la trace de cela parmi tous les cas de traitement des erreurs et de traitement des erreurs, de sorte que vous voudrez peut-être créer un groupe de tests agréable de tests de chemin d'accès satisfaisant qui suivent simplement les spécifications.
la source
En réponse au titre de la question: "Quand écrivez-vous le" vrai "code en TDD?", La réponse est: "presque jamais" ou "très lentement".
Vous parlez comme un étudiant, je vais donc vous répondre comme si vous conseilliez un étudiant.
Vous allez apprendre beaucoup de «théories» et de «techniques» de codage. Ils sont parfaits pour passer le temps sur des cours trop coûteux pour les étudiants, mais ne vous apportent que très peu d’avantages, car vous ne pourriez pas lire un livre deux fois plus vite.
Le travail d'un codeur est uniquement de produire du code. Code qui fonctionne vraiment bien. C'est pourquoi vous, le codeur, planifiez le code dans votre esprit, sur papier, dans une application appropriée, etc., et vous prévoyez de résoudre à l'avance les éventuels défauts / failles en réfléchissant logiquement et latéralement avant de coder.
Mais vous devez savoir comment casser votre application pour pouvoir concevoir un code correct. Par exemple, si vous ne connaissiez pas Little Bobby Table (xkcd 327), vous ne purifieriez probablement pas vos entrées avant de travailler avec la base de données, vous ne pourriez donc pas sécuriser vos données autour de ce concept.
TDD est simplement un flux de travail conçu pour minimiser les bugs dans votre code en créant des tests sur ce qui pourrait mal tourner avant de coder votre application, car le codage peut devenir exponentiellement difficile à mesure que vous introduisez du code et que vous oubliez les bugs auxquels vous pensiez. Une fois que vous pensez que vous avez terminé votre application, vous exécutez les tests et effectuez un boom. Espérons que vos tests détectent des bugs.
TDD n’est pas - comme le pensent certaines personnes - écrire un test, le faire passer avec un minimum de code, écrire un autre test, le réussir avec un minimum de code, etc. Il s’agit plutôt d’un moyen de vous aider à coder en toute confiance. Cet idéal de code de refactorisation continu permettant de faire fonctionner les tests est idiot, mais c’est un bon concept pour les étudiants car cela leur donne une sensation de bien-être lorsqu’ils ajoutent une nouvelle fonctionnalité et qu’ils apprennent toujours à coder ...
Veuillez ne pas tomber dans ce piège et voir votre rôle de codage pour ce qu'il est - le travail d'un codeur est uniquement de produire du code. Code qui fonctionne vraiment bien. Maintenant, souvenez-vous que vous serez à l'heure en tant que codeur professionnel et que votre client ne se souciera pas de savoir si vous avez écrit 100 000 assertions, ou 0. Ils veulent juste un code qui fonctionne. Vraiment bien, en fait.
la source