TDD: est-ce que je le fais bien?

14

Je suis un nouveau programmeur (j'apprends seulement depuis environ un an) et dans mon objectif de devenir meilleur dans ce domaine, je viens tout juste d'apprendre le TDD. Je voulais prendre l'habitude de l'utiliser car cela semble très utile. Je voulais vérifier et m'assurer de l'utiliser correctement.

Qu'est-ce que je fais:

  1. Pensez à une nouvelle méthode dont j'ai besoin.
  2. Créez un test pour cette méthode.
  3. Échec du test.
  4. Méthode d'écriture.
  5. Test de passage.
  6. Méthode de refactorisation.
  7. Répéter.

Je fais cela pour CHAQUE méthode que j'écris, y en a-t-il avec qui je ne devrais pas m'embêter? Plus tard, je pense généralement à un moyen de tester mes méthodes déjà existantes d'une manière ou d'une situation différente. Dois-je faire ces nouveaux tests auxquels je pense, ou puisque chaque méthode a déjà son propre test, ne devrais-je pas m'embêter? Puis-je être PLUS de tester mon code, je suppose que c'est ma principale préoccupation en demandant cela.

ÉDITER

Aussi, c'était quelque chose que je me demandais. Lorsque vous faites quelque chose comme faire une interface graphique, le TDD serait-il nécessaire dans cette situation? Personnellement, je ne vois pas comment j'écrirais des tests pour ça.

cgasser
la source
5
Vous le faites déjà beaucoup mieux que des professionnels chevronnés qui disent tout tester (mais pas).
yannis
Ce que vous décrivez n'est pas l' esprit du TDD.
1
Vous voudrez peut-être examiner ATDD ou BDD.
dietbuddha
Commencez peut-être plus haut - pensez à un nouveau module dont vous avez besoin.

Réponses:

16

Ce que vous décrivez comme un flux de travail n'est pas, à mon avis, l' esprit du TDD.

Le synopsis du livre de Kent Becks sur Amazon dit:

Le développement piloté par les tests vise tout simplement à éliminer la peur dans le développement d'applications.Bien qu'une certaine peur soit saine (souvent considérée comme une conscience qui dit aux programmeurs de "faire attention!"), L'auteur pense que les sous-produits de la peur incluent les programmeurs hésitants, grincheux et peu communicatifs qui sont incapables d'absorber les critiques constructives. Lorsque les équipes de programmation adhèrent à TDD, elles voient immédiatement des résultats positifs. Ils éliminent la peur de leur travail et sont mieux équipés pour relever les défis difficiles auxquels ils sont confrontés. Le TDD élimine les traits provisoires, il apprend aux programmeurs à communiquer et il encourage les membres de l'équipe à rechercher les critiques. Cependant, même l'auteur admet que la grinchosité doit être réglée individuellement! En bref, le principe derrière TDD est que le code doit être continuellement testé et refactorisé.

TDD pratique

Les tests automatisés formels, en particulier les tests unitaires, chaque méthode de chaque classe est tout aussi mauvais un anti-modèle et ne teste rien. Il y a un équilibre à trouver. Ecrivez-vous des tests unitaires pour chaque setXXX/getXXXméthode, ce sont aussi des méthodes!

Les tests peuvent également aider à économiser du temps et de l'argent, mais n'oubliez pas qu'ils coûtent du temps et de l'argent à développer et qu'ils sont du code, donc ils coûtent du temps et de l'argent à maintenir. S'ils s'atrophient par manque d'entretien, ils deviennent un passif plus qu'un avantage.

Comme tout comme ça, il y a un équilibre qui ne peut être défini par personne d'autre que vous-même. Tout dogme dans un sens ou dans l'autre est probablement plus faux que correct.

Une bonne métrique est le code qui est essentiel à la logique métier et sujet à des modifications fréquentes en fonction des exigences changeantes. Ces choses nécessitent des tests formels qui sont automatisés, ce serait un gros retour sur investissement.

Vous allez avoir beaucoup de mal à trouver de nombreux magasins professionnels qui fonctionnent de cette façon. Il n'est tout simplement pas logique de dépenser de l'argent pour tester des choses qui, à toutes fins pratiques, ne changeront jamais après un simple test de fumée. La rédaction de tests unitaires formels automatisés pour les .getXXX/.setXXXméthodes en est un excellent exemple, une perte de temps totale.

Cela fait maintenant deux décennies qu'il a été souligné que les tests de programme peuvent démontrer de manière convaincante la présence de bogues, mais ne peuvent jamais démontrer leur absence. Après avoir cité avec dévotion cette remarque très médiatisée, l'ingénieur logiciel revient à l'ordre du jour et continue d'affiner ses stratégies de test, tout comme l'alchimiste d'antan, qui a continué d'affiner ses purifications chrysocosmiques.

- Edsger W. Djikstra . (Écrit en 1988, il est donc maintenant plus proche de 4,5 décennies.)

Voir aussi cette réponse .

Communauté
la source
1
Cela répond à peu près à ce qui m'inquiétait. Je sentais que je ne devrais pas tester toutes les méthodes comme je l'étais, mais je n'en étais pas sûr. On dirait que j'ai peut-être encore besoin de lire un peu plus sur TDD.
cgasser
@kevincline La plupart du temps setXXX/getXXXne sont pas nécessaires du tout :)
Puce
1
Lorsque vous mémorisez ce getXXX trivial et que vous vous trompez, ou que vous introduisez un chargement paresseux dans votre getXXX et que vous vous trompez, alors vous saurez que parfois vous voulez vraiment tester vos getters.
Frank Shearar
13

Tu es très proche. Essayez de penser de cette manière légèrement différente.

  1. Pensez à un nouveau comportement dont j'ai besoin.
  2. Créez un test pour ce comportement.
  3. Échec du test.
  4. Écrivez une nouvelle méthode ou étendez une méthode existante.
  5. Test de passage.
  6. Code refactor.
  7. Répéter.

Ne créez pas automatiquement de getters et setters pour chaque propriété . Ne pensez pas à une méthode entière et écrivez le (s) test (s) pour couvrir toutes les fonctionnalités . Essayez d'encapsuler les propriétés à l'intérieur de la classe et d'écrire des méthodes pour fournir le comportement dont vous avez besoin. Laissez vos méthodes évoluer vers une bonne conception au lieu d'essayer de les planifier à l'avance. N'oubliez pas que TDD est un processus de conception et non un processus de test. L'avantage qu'il a sur les autres processus de conception est de laisser derrière lui un flux de tests de régression automatisés, plutôt qu'un morceau de papier que vous jetez à la poubelle.

Souvenez-vous également des trois règles de l'oncle Bob en matière de TDD .

  1. Vous n'êtes pas autorisé à écrire un code de production, sauf pour effectuer un test unitaire échoué.
  2. Vous n'êtes pas autorisé à écrire plus d'un test unitaire que ce qui est suffisant pour échouer; et les échecs de compilation sont des échecs.
  3. Vous n'êtes pas autorisé à écrire plus de code de production qu'il n'en faut pour réussir le test unitaire ayant échoué.
pdr
la source
1
@Zexanima: Vous vous débrouillez bien mieux que la plupart d'entre nous après un an. J'essaie juste de vous indiquer l'étape suivante.
pdr
2
Je pense que ces 3 règles que vous liez à; aussi idylliques que cela puisse paraître, sont exceptionnellement dogmatiques et extrêmement irréalistes dans 99% de tous les ateliers de production que quiconque rencontrera.
1
@FrankShearar ou cela peut être vu comme le rassemblement impraticable d'un extrémiste fondamentaliste et totalement ignoré. J'ai travaillé dans des magasins qui avaient cette attitude dogmatique, ils ont pris le dogme à la lettre et ont raté le point; en écrivant des tests qui n'ont testé aucun de leur code réel de manière pratique et en finissant par tester la capacité des frameworks Mocking et Dependency Injection à confondre au mieux ce qui était important.
1
@pdr L'Esprit de quelque chose est diamétralement opposé à la canonisation formelle dogmatique de cette chose. C'est une chose d'avoir une philosophie et une autre de la transformer en religion . Le TDD est plus que jamais parlé en termes religieux dogmatiques en noir et blanc . Que 3 règles sonnent dogmatiques et religieuses dans la présentation et que ce qui est entendu est le test, le test, le mantra du test , pour quelqu'un comme l'OP, ils les prennent littéralement et cela cause plus de mal que de bien. J'ai répliqué à Frank que les déclarations polarisantes peuvent faire plus de mal que de bien à la cause.
2
Mon point était que le dogmatisme vient d' accepter aveuglément quelque chose comme évangile . Prenez l'énoncé polarisant, essayez-le, faites-le sortir de votre zone de confort. Vous ne pouvez pas évaluer les compromis impliqués dans le TDD si vous n'essayez pas l'approche extrême en 3 points, tout ou rien, car vous n'aurez pas de données .
Frank Shearar
5

Peu de choses à ajouter aux réponses des autres:

  1. Il y a une telle chose que sur-tester. Vous voulez vous assurer que vos tests unitaires se chevauchent le moins possible. Il est inutile que plusieurs tests vérifient les mêmes conditions dans le même morceau de code. En revanche, lorsque vous refactorisez votre code de production et que vous avez de nombreux tests qui chevauchent cette section, vous devrez revenir en arrière et corriger tous ces tests. Alors que s'ils ne se chevauchent pas, un changement ne pourra au plus casser qu'un seul test.

  2. Tout simplement parce que vous avez pensé à une meilleure façon d'écrire un test, je ne voudrais pas y retourner et commencer à le réécrire. Cela revient aux individus qui continuent d'écrire et de réécrire la même classe / fonction parce qu'ils essaient de la rendre parfaite. Ce ne sera jamais parfait, alors passez à autre chose. Lorsque vous découvrez une meilleure méthode, gardez-la à l'esprit (ou ajoutez-la aux commentaires du test). La prochaine fois que vous êtes là-bas et que vous voyez un avantage immédiat à passer à la nouvelle façon, c'est le moment de refactoriser. Sinon, si la fonctionnalité est terminée et que vous avez continué et que tout fonctionne, laissez-la fonctionner.

  3. TDD se concentre sur la fourniture d'une valeur immédiate, et non simplement sur le fait que chaque fonction est testable. Lorsque vous ajoutez des fonctionnalités, commencez par demander «de quoi a besoin le client». Définissez ensuite une interface pour donner au client ce dont il a besoin. Ensuite, implémentez tout ce qu'il faut pour réussir le test. TDD est presque comme tester des scénarios de cas d'utilisation (y compris tous les "what-ifs"), plutôt que de simplement coder des fonctions publiques et de tester chacun.

  4. Vous avez posé des questions sur le test du code GUI. Recherchez les modèles "Humble Dialog" et "MVVM". L'idée derrière ces deux est que vous créez un ensemble de classes de "modèle de vue", qui n'ont pas réellement de logique spécifique à l'interface utilisateur. Cependant, ces classes auront toute la logique métier qui fait généralement partie de votre interface utilisateur et ces classes doivent pouvoir être testées à 100%. Ce qui reste est un shell d'interface utilisateur très mince et oui, généralement ce shell est laissé sans couverture de test, mais à ce stade, il ne devrait presque pas avoir de logique.

  5. Si vous avez une grande partie du code existant, comme peu d'autres l'ont suggéré, vous ne devriez pas commencer à ajouter des tests unitaires absolument partout. Cela vous prendra une éternité et vous n'aurez aucun avantage à ajouter des tests unitaires à 80% des classes qui sont stables et ne changeront pas dans un avenir proche (ou pas si proche). Cependant, pour de nouveaux travaux, je trouve que l'utilisation du développement TDD avec TOUT le code est extrêmement bénéfique. Non seulement vous vous retrouvez avec une suite de tests automatisés lorsque vous avez terminé, mais le développement réel présente d'énormes avantages:

    • En considérant la testabilité, vous écrirez du code moins couplé et plus modulaire
    • En considérant votre contrat public avant toute autre chose, vous vous retrouverez avec des interfaces publiques beaucoup plus propres
    • Lorsque vous écrivez du code, la vérification de nouvelles fonctionnalités prend des millisecondes par rapport à l'exécution de l'intégralité de votre application et à la tentative de forcer l'exécution sur le bon chemin. Mon équipe publie toujours du code de gestion des erreurs qui n'a même pas été exécuté UNE FOIS simplement parce qu'ils n'ont pas pu obtenir le bon ensemble de conditions. Il est étonnant de voir combien de temps nous perdons lorsque, plus tard dans l'assurance qualité, ces conditions se produisent. Et oui, une grande partie de ce code est ce que quelqu'un aurait considéré comme "pas un domaine où il y aurait beaucoup de changements à l'avenir une fois les tests de fumée terminés".
DXM
la source
1

Il existe certaines méthodes qui ne sont pas testées, à savoir ces tests. Cependant, il y a quelque chose à dire pour certains tests ajoutés après l'écriture du code initial, tels que les conditions aux limites et d'autres valeurs afin qu'il puisse y avoir plusieurs tests sur une seule méthode.

Bien que vous puissiez sur-tester votre code, cela vient généralement là où quelqu'un veut tester toutes les permutations possibles des entrées, ce qui ne ressemble pas vraiment à ce que vous faites. Par exemple, si vous avez une méthode qui prend un caractère, écrivez-vous un test pour chaque valeur possible qui pourrait être entrée? Ce serait là que vous auriez à surestimer, OMI.

JB King
la source
Ahh d'accord. Ce n'est pas ce que je fais. Je finis généralement par penser à une situation différente, je pourrais tester mes méthodes un peu plus tard après avoir déjà fait leur test initial. Je m'assurais juste que ces tests «supplémentaires» valaient la peine d'être faits, ou si c'était fini.
cgasser
Si vous travaillez par petits incréments, vous pouvez généralement être raisonnablement sûr que votre test fonctionne réellement. En d'autres termes, l'échec d'un test (pour la bonne raison!) Est en soi un test. Mais ce niveau de "raisonnablement sûr" ne sera pas aussi élevé que le code testé.
Frank Shearar
1

En général, vous le faites correctement.

Les tests sont du code. Donc, si vous pouvez améliorer le test, allez-y et refactorisez-le. Si vous pensez qu'un test peut être amélioré, allez-y et changez-le. N'ayez pas peur de remplacer un test par un meilleur.

Je recommande lors du test de votre code, d'éviter de spécifier comment le code est censé faire ce qu'il fait. Les tests devraient examiner les résultats des méthodes. Cela vous aidera à refactoriser. Certaines méthodes n'ont pas besoin d'être testées explicitement (c.-à-d. De simples getters et setters) car vous les utiliserez pour vérifier les résultats d'autres tests.

Schleis
la source
J'écrivais aussi des tests pour les getters et les setters, donc merci pour cette astuce. Cela me fera économiser du travail inutile.
cgasser
"Certaines méthodes n'ont pas besoin d'être testées explicitement (c'est-à-dire de simples getters et setters)" - Vous n'avez jamais copié / collé un getter et un setter et oublié de changer le nom du champ derrière lui? La chose à propos du code simple est qu'il nécessite des tests simples - combien de temps économisez-vous vraiment?
pdr
Je ne veux pas dire que la méthode n'est pas testée. Il est simplement vérifié en confirmant que d'autres méthodes sont définies ou lors de la configuration réelle d'un test. Si le getter ou le setter ne fonctionne pas correctement, le test échouera car les propriétés n'ont pas été définies correctement. Vous les faites tester gratuitement, implicitement.
Schleis
Les tests d'obtention et de définition ne prennent pas longtemps, donc je continuerai probablement à les faire. Cependant, je ne copie et ne colle jamais aucun de mon code, je ne rencontre donc pas ce problème.
cgasser
0

Mon opinion sur TDD est que l'outillage a créé un monde de développeurs de style «pointer et cliquer». Ce n'est pas parce que les outils créent un talon de test pour chaque méthode que vous devez écrire des tests pour chaque méthode. Certaines personnes «renomment» TDD en BDD (développement axé sur le comportement) où les tests sont beaucoup plus larges et destinés à tester le comportement de la classe, et non chaque petite méthode.

Si vous concevez vos tests pour tester la classe comme elle est destinée à être utilisée, vous commencez à gagner certains avantages, en particulier lorsque vous commencez à écrire des tests qui exercent un peu plus que chaque méthode, en particulier lorsque vous commencez à tester l'interaction de ces méthodes. Je suppose que vous pourriez y penser comme de l'écriture de tests pour une classe, plutôt que de méthodes. Dans tous les cas, vous devez toujours écrire des «tests d'acceptation» qui exercent la combinaison de méthodes pour vous assurer qu'il n'y a pas de contradictions ou de conflits dans la façon dont ils sont utilisés ensemble.

Ne confondez pas TDD avec les tests - ce n'est pas le cas. TDD est conçu pour que vous écriviez du code pour répondre à vos besoins et non pour tester les méthodes. C'est un point subtil mais important qui est souvent perdu pour les personnes qui écrivent aveuglément du code de test pour chaque méthode. C'est que vous devriez écrire des tests pour vous assurer que votre code fait ce que vous voulez qu'il fasse, pas que le code que vous avez écrit fonctionne comme il est censé le faire.

Il existe de bons liens vers la droite sur BDD v TDD. Vérifie-les.

gbjbaanb
la source
0

Lorsque vous commencez à apprendre le TDD, oui, vous devez suivre aveuglément l'approche dogmatique de ne pas écrire une seule ligne de code, sauf pour réussir un test échoué, et écrire suffisamment de test pour échouer (et échouer pour la bonne raison / la raison attendue) .

Une fois que vous avez appris ce qu'est le TDD, ALORS vous pouvez décider que certains types de choses ne valent pas la peine d'être testés. C'est la même approche que vous devez suivre pour tout, et les arts martiaux japonais appellent cela " shuhari ". (Le lien explique également comment on peut progresser à travers les étapes de l'apprentissage sans enseignant, ce qui est, je suppose, la façon dont la plupart des gens doivent apprendre.)

Frank Shearar
la source
0

Je crois que vous surestimez.

Je pratique le TDD depuis de nombreuses années, et d'après mon expérience, lorsque le TDD est effectué efficacement, vous obtenez deux avantages principaux:

  • Fournir une rétroaction rapide
  • Activer le refactoring

Fournir une rétroaction rapide

En particulier avec les langages dynamiques, je peux exécuter les tests pertinents en moins d'une seconde. Et j'ai des observateurs de système de fichiers qui exécutent ces tests automatiquement lorsqu'un fichier source est modifié sur le disque. Ainsi, je n'ai pratiquement pas de temps d'attente pour les tests, et je sais immédiatement si le code que j'écris a fait comme prévu. TDD conduit ainsi à une manière de travailler très efficace.

Activer le refactoring

Si vous avez une bonne suite de tests, vous pouvez refactoriser en toute sécurité, car vous obtenez de nouvelles informations sur la façon dont le système doit être conçu.

Une bonne suite de tests vous permet de déplacer la responsabilité dans votre code, tout en étant sûr que le code fonctionne comme prévu après le déplacement. Et vous devriez pouvoir le faire avec de petites modifications du code de test.

Si vous écrivez des tests pour chaque méthode de votre système, il y a de fortes chances que vous ne puissiez pas facilement refactoriser votre code, chaque refactoreur de votre code nécessitera des modifications massives du code de test. Et pouvez-vous même être sûr que le code de test fonctionne toujours comme prévu? Ou avez-vous accidentellement introduit un bogue dans le code de test, ce qui conduit par conséquent à un bogue dans le code de production?

Si toutefois, comme le suggère également la réponse de pdr, vous vous concentrez sur le comportement plutôt que sur les méthodes lors de l'écriture des tests, vous aurez des tests qui nécessiteront beaucoup moins de changements lors de la refactorisation du système.

Ou, comme le dit Ian Cooper dans cette présentation (je l'ai cité de mémoire, donc peut-être pas correctement cité):

Votre raison d'écrire un nouveau test devrait être d'ajouter un nouveau comportement, pas d'ajouter une nouvelle classe

Pete
la source
-2

Vous devez tester chaque méthode publique .

Le problème ici est que si vos méthodes publiques sont très petites, vous exposez probablement trop d'informations. La pratique courante d'exposer chaque propriété comme getXXX()rompt réellement l'encapsulation.

Si vos méthodes publiques sont en fait le comportement de la classe, vous devez les tester. Sinon, ce ne sont pas de bonnes méthodes publiques.

EDIT: la réponse de pdr est beaucoup plus complète que la mienne.

Puce
la source