Je suis assez nouveau sur TDD et j'ai des problèmes lors de la création de mon premier test avant le code d'implémentation. Sans aucun cadre pour le code d'implémentation, je suis libre d'écrire mon premier test comme je veux, mais il semble toujours être entaché par ma façon de penser Java / OO sur le problème.
Par exemple, dans mon Github ConwaysGameOfLifeExemple le premier test que j'ai écrit (rule1_zeroNeighbours), j'ai commencé par créer un objet GameOfLife qui n'avait pas encore été implémenté; a appelé une méthode set qui n'existait pas, une méthode step qui n'existait pas, une méthode get qui n'existait pas, puis a utilisé une assertion.
Les tests ont évolué au fur et à mesure que j'écrivais plus de tests et refactorisais, mais à l'origine, cela ressemblait à ceci:
@Test
public void rule1_zeroNeighbours()
{
GameOfLife gameOfLife = new GameOfLife();
gameOfLife.set(1, 1, true);
gameOfLife.step();
assertEquals(false, gameOfLife.get(1, 1));
}
Cela semblait étrange car je forçais la conception de l'implémentation en fonction de la façon dont j'avais décidé à ce stade précoce d'écrire ce premier test.
Dans la façon dont vous comprenez le TDD, est-ce correct? Je semble suivre les principes TDD / XP en ce que mes tests et implémentations ont évolué au fil du temps avec le refactoring, et donc si cette conception initiale s'était révélée inutile, elle aurait été ouverte à changement, mais j'ai l'impression de forcer une direction sur la solution en commençant de cette façon.
Sinon, comment les gens utilisent TDD? J'aurais pu passer par plus d'itérations de refactoring en commençant avec aucun objet GameOfLife, seulement des primitives et des méthodes statiques mais cela semble trop artificiel.
la source
Réponses:
Je pense que c'est le point clé de votre question, la question de savoir si cela est souhaitable dépend de si vous vous penchez vers l'idée de codeninja que vous devriez concevoir à l'avance puis utiliser TDD pour remplir l'implémentation, ou l'idée de durron que les tests devraient être impliqués dans chasser la conception ainsi que la mise en œuvre.
Je pense que celui que vous préférez (ou où vous vous situez au milieu) est quelque chose que vous devez découvrir par vous-même en tant que préférence. Il est utile de comprendre les avantages et les inconvénients de chaque approche. Il y en a probablement beaucoup, mais je dirais que les principaux sont:
Pro Upfront Design
Conception Pro Test-Driving
En construisant votre implémentation autour d'un client de votre code (vos tests), vous obtenez l'adhésion à YAGNI presque gratuitement, tant que vous ne commencez pas à écrire des cas de test inutiles. Plus généralement, vous obtenez une API conçue autour de son utilisation par un consommateur, ce qui est finalement ce que vous voulez.
L'idée de dessiner un tas de diagrammes UML avant d'écrire n'importe quel code puis de simplement combler les lacunes est agréable, mais rarement réaliste. Dans Code Complete de Steve McConnell, le design est décrit comme un "problème méchant" - un problème que vous ne pouvez pas comprendre sans d'abord le résoudre au moins partiellement. Ajoutez à cela le fait que le problème sous-jacent lui-même peut changer à travers des exigences changeantes, et ce modèle de conception commence à se sentir un peu désespéré. Les essais de conduite vous permettent de mordre un morceau de travail à la fois - dans la conception, pas seulement la mise en œuvre - et de savoir qu'au moins pendant la durée de vie du rouge au vert, cette tâche sera toujours à jour et pertinente.
En ce qui concerne votre exemple particulier, comme le dit Durron, si vous optiez pour une approche consistant à éliminer la conception en écrivant le test le plus simple, en consommant l'interface minimale possible, vous commenceriez probablement avec une interface plus simple que celle de votre extrait de code .
la source
Afin d'écrire le test en premier lieu, vous devez concevoir l'API que vous allez ensuite implémenter. Vous avez déjà commencé du mauvais pied en écrivant votre test pour créer l' objet entier
GameOfLife
et en l'utilisant pour implémenter votre test.De tests unitaires pratiques avec JUnit et Mockito :
Votre test ne fait pas beaucoup de tentative de conception d'une API. Vous avez configuré un système avec état où toutes les fonctionnalités sont contenues dans la
GameOfLife
classe externe .Si je devais écrire cette application, je penserais plutôt aux pièces que je veux construire. Par exemple, je pourrais créer une
Cell
classe, écrire des tests pour cela, avant de passer à l'application plus grande. Je voudrais certainement créer une classe pour la structure de données "infinie dans toutes les directions" qui est nécessaire pour implémenter correctement Conway, et tester cela. Une fois que tout cela a été fait, je penserais à écrire la classe globale qui a unemain
méthode et ainsi de suite.Il est facile de passer outre l'étape «écrire un test qui échoue». Mais l'écriture du test d'échec qui fonctionne comme vous le souhaitez est au cœur de TDD.
la source
boolean
, cette conception serait certainement pire pour les performances. À moins qu'il doive être extensible à l'avenir à d'autres automates cellulaires avec plus de deux états?Il y a une école de pensée différente à ce sujet.
Certains disent: le test ne compile pas est une erreur - allez corriger le plus petit code de production disponible.
Certains disent: il est bon d'écrire le test d'abord vérifier s'il aspire (ou non) la fourmi puis créer les classes / méthodes manquantes
Avec la première approche, vous êtes vraiment dans un cycle rouge-vert-refactor. Avec le second, vous avez un aperçu un peu plus large de ce que vous souhaitez réaliser.
C'est à vous de choisir la façon dont vous travaillez. À mon humble avis, les deux approches sont valides.
la source
Même lorsque j'implémente quelque chose d'une manière «hack it together», je pense toujours aux classes et aux étapes qui seront impliquées dans l'ensemble du programme. Vous avez donc réfléchi et écrit ces idées de conception comme test en premier - c'est génial!
Continuez maintenant à parcourir les deux implémentations pour réaliser ce test initial, puis ajoutez d'autres tests pour améliorer et étendre la conception.
Ce qui pourrait vous aider, c'est d'utiliser du concombre ou similaire pour écrire vos tests.
la source
Avant de commencer à écrire vos tests, vous devez réfléchir à la façon de concevoir votre système. Vous devez consacrer un temps considérable à votre phase de conception. Si vous l'avez fait, vous n'aurez pas cette confusion sur TDD.
TDD n'est qu'un lien d' approche de développement : TDD
1. Ajoutez un test
2. Exécutez tous les tests et voyez si le nouveau échoue
3. Écrivez du code
4. Exécutez les tests
5. Code de refactorisation
6. Répétez
TDD vous aide à couvrir toutes les fonctionnalités requises ce que vous avez prévu avant de commencer à développer votre logiciel. lien: Avantages
la source
Je n'aime pas les tests de niveau système écrits en java ou C # pour cette raison. Regardez SpecFlow pour c # ou l'un des framework de test basé sur Cucumber pour java (peut-être JBehave). Vos tests peuvent alors ressembler davantage à ceci.
Et vous pouvez changer la conception de votre objet sans avoir à modifier tous vos tests système.
(Les tests unitaires «normaux» sont parfaits pour tester des classes individuelles.)
Quelles sont les différences entre les frameworks BDD pour Java?
la source