Comment obtenir l'API initiale à l'aide de TDD?

12

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.

Vytautas Mackonis
la source
3
30 tests sur une méthode? On dirait que cette méthode est beaucoup trop complexe ou que vous écrivez trop de tests.
Minthos
Eh bien, j'ai peut-être exagéré un peu pour exprimer mon point. Après avoir vérifié le code, j'ai généralement moins de 10 méthodes par test, la plupart d'entre elles étant inférieures à 5. Mais toute la partie "revenir en arrière et les changer à la main" a été assez frustrante.
Vytautas Mackonis
6
@Minthos: Je peux penser à 6 tests sur le haut de ma tête contre lesquels toute méthode prenant une chaîne échouera souvent ou fonctionnera mal lors d'une première écriture (nulle, vide, trop longue, non localisée correctement, mauvaise mise à l'échelle des performances) . De même pour les méthodes prenant une collection. Pour une méthode non triviale, 30 sons gros, mais pas trop irréalistes.
Steven Evers

Réponses:

13

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.

Si vous ne réfléchissez pas à l'architecture, si ce que vous faites à la place est d'ignorer l'architecture et de lancer des tests ensemble et de les faire passer, vous détruisez ce qui permettra au bâtiment de rester en place parce que c'est la concentration sur le structure du système et décisions de conception solides qui ont aidé le système à maintenir son intégrité structurelle.

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.

Robert Harvey
la source
Le fait est que, même avec une «planification raisonnable», vous vous attendez à ce que beaucoup de choses changent. Je laisse habituellement environ 80% de mon architecture initiale intacte avec quelques changements entre les deux. Ces 20% sont ce qui me dérange.
Vytautas Mackonis
2
Je pense que c'est juste la nature du développement logiciel. Vous ne pouvez pas vous attendre à obtenir l'architecture complète dès la première tentative.
Robert Harvey
2
+1, et ce n'est pas contraire à TDD. TDD commence lorsque vous commencez à écrire du code, précisément à la fin de la conception. TDD peut vous aider à voir ce que vous avez manqué dans votre conception, vous permettant de refactoriser la conception et la mise en œuvre, et de continuer.
Steven Evers
2
En fait, selon Bob (et je suis entièrement d'accord avec lui), l'écriture de code est également une conception. Avoir une architecture de haut niveau est certainement nécessaire mais la conception ne s'arrête pas lorsque vous écrivez votre code.
Michael Brown
Vraiment une bonne réponse qui frappe le clou sur la tête. Je vois tellement de gens, à la fois pour et contre TDD qui semblent prendre "pas de gros design à l'avant" comme "ne pas concevoir du tout, juste coder" alors que c'est en fait une grève contre les étapes de conception de la cascade insensée d'autrefois. La conception est toujours un bon investissement en temps et est cruciale pour la réussite de tout projet non trivial.
sara
3

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.

Joppe
la source
1

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).

AlexBottoni
la source
0

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

SnoopDougieDoug
la source