Cela pourrait être une question assez stupide que je suis à mes premiers essais de TDD. J'ai adoré le sentiment de confiance qu'il apporte et généralement une meilleure structure de mon code mais quand j'ai commencé à l'appliquer à quelque chose de plus grand que des exemples de jouets d'une classe, j'ai rencontré des difficultés.
Supposons que vous écrivez une sorte de bibliothèque. Vous savez ce qu'il doit faire, vous connaissez une manière générale de le mettre en œuvre (en termes d'architecture), mais vous continuez à «découvrir» que vous devez apporter des modifications à votre API publique pendant que vous codez. Peut-être devez-vous transformer cette méthode privée en modèle de stratégie (et devez maintenant passer une stratégie simulée dans vos tests), peut-être avez-vous égaré une responsabilité ici et là et divisé une classe existante.
Lorsque vous améliorez le code existant, TDD semble un très bon choix, mais lorsque vous écrivez tout à partir de zéro, l'API pour laquelle vous écrivez des tests est un peu "floue" à moins que vous ne fassiez un gros design à l'avance. Que faites-vous lorsque vous avez déjà 30 tests sur la méthode dont la signature (et pour cette partie, le comportement) a changé? C'est beaucoup de tests à changer une fois qu'ils s'additionnent.
Réponses:
Ce que vous appelez «une grande conception d'avance», j'appelle «une planification judicieuse de l'architecture de votre classe».
Vous ne pouvez pas développer une architecture à partir de tests unitaires. Même l' oncle Bob le dit.
http://s3.amazonaws.com/hanselminutes/hanselminutes_0171.pdf , page 4
Je pense qu'il serait plus judicieux d'aborder TDD dans une perspective de validation de votre conception structurelle. Comment savez-vous que le design est incorrect si vous ne le testez pas? Et comment vérifiez-vous que vos modifications sont correctes sans modifier également les tests d'origine?
Le logiciel est "logiciel" précisément parce qu'il est susceptible de changer. Si vous n'êtes pas à l'aise avec l' ampleur des modifications, continuez à acquérir de l'expérience en conception architecturale et le nombre de modifications que vous devrez apporter à vos architectures d'application diminuera avec le temps.
la source
Si vous faites TDD. Vous ne pouvez pas changer la signature et le comportement sans l'avoir conduit par des tests. Ainsi, les 30 tests qui ont échoué ont été supprimés dans le processus ou modifiés / refactorisés avec le code. Ou ils sont désormais obsolètes, sûrs à supprimer.
Vous ne pouvez pas ignorer les 30 fois rouges dans votre cycle de refonte rouge-vert?
Vos tests doivent être refactorisés à côté de votre code de production. Si vous pouvez vous le permettre, réexécutez tous les tests après chaque modification.
N'ayez pas peur de supprimer les tests TDD. Certains tests finissent par tester des blocs de construction pour arriver au résultat souhaité. Le résultat souhaité au niveau fonctionnel est ce qui compte. Les tests autour des étapes intermédiaires de l'algorithme que vous avez choisi / inventé peuvent ou peuvent ne pas avoir beaucoup de valeur lorsqu'il existe plus d'un moyen d'atteindre le résultat ou que vous êtes initialement tombé dans une impasse.
Parfois, vous pouvez créer des tests d'intégration décents, les conserver et supprimer le reste. Cela dépend quelque peu si vous travaillez à l'envers ou de haut en bas et la taille des étapes que vous prenez.
la source
Comme Robert Harvey vient de le dire, vous essayez probablement d'utiliser TDD pour quelque chose qui devrait être géré par un outil conceptuel différent (c'est-à-dire: "conception" ou "modélisation").
Essayez de concevoir (ou de "modéliser") votre système d'une manière assez abstraite ("générale", "vague"). Par exemple, si vous devez modéliser une voiture, il vous suffit d'avoir une classe de voitures avec une méthode et un champ vagues, comme startEngine () et int sièges. C'est-à-dire: décrivez ce que vous voulez exposer au public , pas comment vous voulez le mettre en œuvre. Essayez d'exposer uniquement les fonctionnalités de base (lecture, écriture, démarrage, arrêt, etc.) et laissez le code client élaborer dessus (prepareMyScene (), killTheEnemy (), etc.).
Notez vos tests en supposant cette interface publique simple.
Modifiez le comportement interne de vos classes et méthodes chaque fois que vous en avez besoin.
Si et quand vous devez changer votre interface publique et votre suite de tests, arrêtez-vous et réfléchissez. Il s'agit très probablement d'un signe qu'il y a un problème dans votre API et dans votre conception / modélisation.
Il n'est pas rare de changer une API. La plupart des systèmes dans leur version 1.0 mettent explicitement en garde les programmeurs / utilisateurs contre d'éventuelles modifications de leur API. Malgré cela, un flux continu et incontrôlé de changements d'API est un signe clair de mauvaise conception (ou totalement manquante).
BTW: Vous ne devriez généralement avoir qu'une poignée de tests par méthode. Une méthode, par définition, devrait implémenter une "action" clairement définie sur une sorte de données. Dans un monde parfait, cela devrait être une seule action qui correspond à un seul test. Dans le monde réel, il n'est pas inhabituel (et pas faux) d'avoir peu de "versions" différentes de la même action et peu de tests correspondants différents. Pour sûr, vous devez éviter d'avoir 30 tests sur la même méthode. C'est un signe clair que la méthode essaie d'en faire trop (et son code interne est devenu hors de contrôle).
la source
Je le regarde du point de vue de l'utilisateur. Par exemple, si vos API me permettaient de créer un objet Person avec un nom et un âge, il valait mieux qu'il y ait un constructeur et des méthodes d'accesseur Person (nom de chaîne, int age) pour le nom et l'âge. Il est simple de créer des cas de test pour de nouvelles personnes avec et sans nom et âge.
doug
la source