Pourquoi ne pas écrire tous les tests en même temps en TDD?


55

Le cycle Rouge - Vert - Refactor pour TDD est bien établi et accepté. Nous écrivons un test unitaire en échec et le faisons passer aussi simplement que possible. Quels sont les avantages de cette approche par rapport à l'écriture de nombreux tests unitaires échouant pour une classe et à leur exécution simultanée?

La suite de tests vous protège toujours contre l'écriture de code incorrect ou les erreurs de refactoring, alors quel est le problème? Parfois, il est plus facile d'écrire d'abord tous les tests d'une classe (ou d'un module) sous forme de «vidage de cerveau» pour consigner rapidement tous les comportements attendus en une seule fois.


20
Faites ce qui vous convient le mieux (après quelques expérimentations). Suivre aveuglément le dogme n’est jamais une bonne chose.
Michael Borgwardt

6
J'ose dire que l'écriture de tous vos tests à la fois ressemble beaucoup à l'écriture de tout votre code d'application à la fois.
Michael Haren

1
@MichaelHaren Tous les tests pour une classe (ou un module fonctionnel), désolés pour la confusion
RichK

3
Aborder le problème du "brain dump": Parfois, il y a des points dans les tests / codage lorsque vous vous rendez compte de la nécessité de plusieurs tests d'entrée spécifiques, et il y a une tendance à vouloir capitaliser sur la clarté de cette réalisation avant de vous laisser distraire. minuties du codage. Je gère généralement cela en maintenant une liste séparée (par exemple, Mylyn) ou avec une liste de commentaires dans la classe de test contenant différentes choses que je veux me rappeler de tester (par exemple, // test null case). Cependant, je ne code toujours qu'un seul test à la fois, et je me retrouve systématiquement dans la liste.
Sam Goldberg

1
Eh bien, je ne sais pas pourquoi personne ne l’a mentionné, mais vous ne POUVEZ PAS écrire tous les tests à la fois. Écrire tous les tests avant la main est exactement la même chose que faire BDUF . Et qu'est-ce que l'histoire nous a appris sur BDUF? Cela ne fonctionne presque jamais.
Songo

Réponses:


50

La conception pilotée par les tests consiste à obtenir votre API correcte, pas le code.

L’écriture des tests échoués les plus simples a pour avantage d’avoir votre API (que vous concevez à la volée) aussi simple que possible. À l'avant.

Toutes les utilisations futures (qui sont les prochains tests que vous écrivez) iront de la conception simple initiale au lieu d’une conception sous-optimale prenant en charge des cas plus complexes.


Excellents points! Nous sommes parfois tellement plongés dans le test du code que nous ignorons parfois à quel point l’API et le modèle de domaine peuvent être importants avant même d’écrire votre premier test.
maple_shaft

+1 pour répondre à l'intention du développement piloté par les tests .
Joshua Drake le

76

Lorsque vous écrivez un test, vous vous concentrez sur une chose.
Avec de nombreux tests, vous étalez votre attention sur de nombreuses tâches. Ce n'est donc pas une bonne idée.


8
Qui voudrait réduire cela?!
CaffGeek

6
@Chad je n'étais pas le vote bas, mais je crois que cette réponse manque l'évidence. Le développement piloté par les tests consiste à utiliser le (s) test (s) pour piloter la conception du code. Vous écrivez le test individuellement afin de faire évoluer la conception, et pas seulement pour la testabilité. S'il ne s'agissait que d'artefacts de test, la réponse serait bonne, mais certaines informations cruciales sont manquantes.
Joshua Drake le

7
Je n'ai pas voté vers le bas mais J'y ai pensé. C'est une réponse beaucoup trop brève à une question complexe.
Mark Weston

2
+1 pour se concentrer sur une chose à la fois, notre capacité à effectuer plusieurs tâches est surestimée.
cctan

C'est la réponse la plus simple qui puisse fonctionner.
DNA

27

L'une des difficultés rencontrées lors de l'écriture de tests unitaires est que vous écrivez du code, ce qui en soi peut être sujet à des erreurs. Il est également possible que vous deviez éventuellement avoir besoin de modifier vos tests ultérieurement à la suite d'un effort de refactoring lors de l'écriture de votre code d'implémentation. Avec TDD, cela signifie que vous risquez de vous perdre un peu trop avec vos tests et de constater que vous-même avez besoin de réécrire beaucoup de code de test essentiellement "non testé" à mesure que votre implémentation évolue au cours du projet. Une façon d'éviter ce genre de problème est simplement de se concentrer sur une seule chose à la fois. Cela vous assure de minimiser l'impact de tout changement sur vos tests.

Bien sûr, cela dépend en grande partie de la façon dont vous écrivez votre code de test. Rédigez-vous un test unitaire pour chaque méthode ou rédigez-vous des tests axés sur les fonctionnalités / exigences / comportements? Une autre approche pourrait consister à utiliser une approche axée sur le comportement avec un cadre approprié et à se concentrer sur la rédaction de tests comme s'il s'agissait de spécifications. Cela impliquerait soit d'adopter la méthode BDD, soit d'adapter le test BDD si vous souhaitez vous en tenir à la TDD de manière plus formelle. Sinon, vous pouvez vous en tenir entièrement au paradigme TDD, tout en modifiant la façon dont vous écrivez les tests afin qu'au lieu de vous concentrer uniquement sur les méthodes de test, vous testez les comportements de manière plus générale afin de satisfaire les spécificités des fonctionnalités que vous implémentez.

Indépendamment de l’approche spécifique que vous prenez, dans tous les cas que j’ai décrits ci-dessus, vous utilisez une approche test d’abord; alors, même si vous tentez de télécharger simplement votre cerveau dans une suite de tests ravissante, vous souhaitez également tentation de faire plus que ce qui est absolument nécessaire. Chaque fois que je suis sur le point de démarrer une nouvelle suite de tests, je commence à répéter YAGNI et parfois même à l'insérer dans un commentaire dans mon code afin de me rappeler de rester concentré sur ce qui est immédiatement important et de ne faire que le minimum exigences de la fonctionnalité que je suis sur le point d'implémenter. S'en tenir à Red-Green-Refactor aide à s'assurer que vous le ferez.


Je suis heureux que vous ayez souligné la distinction entre la façon dont on écrit leur code de test. Certains aiment écrire un seul test unitaire qui couvre toutes les possibilités réalistes d’entrées dans une fonction ou une méthode. D'autres adoptent une approche plus BDD avec leurs tests unitaires. Cette distinction est importante pour déterminer si l’écriture de toute une série de tests est importante ou non. Grande perspicacité!
maple_shaft

17

Je pense qu'en faisant cela, vous ratez le processus du TDD. En écrivant simplement tous vos tests au début, vous ne suivez pas vraiment le processus de développement utilisant TDD. Vous devinez simplement quels tests vous aurez besoin. Ce sera un ensemble de tests très différent de ceux que vous finirez par écrire si vous les exécutez un par un au fur et à mesure que vous développez votre code. (À moins que votre programme ne soit de nature triviale.)


1
La plupart des applications d'entreprise et d'entreprise sont techniquement de nature triviale et, vu que la plupart des applications sont des applications d'entreprise et d'entreprise, la plupart des applications sont donc également triviales par nature.
maple_shaft

5
@maple_shaft - la technologie peut être triviale, mais les règles commerciales ne le sont pas. Essayez de créer une application pour 5 gestionnaires qui ont tous des exigences différentes et refusent d’écouter des BS sur votre design simpliste, élégant, moins-plus-minimiste.
JeffO

5
@JeffO 1) Ce n'est pas BS. 2) Un design élégant et minimaliste nécessite de bonnes compétences en développement logiciel. 3) La capacité d'atténuer les exigences de 5 gestionnaires différents qui n'ont pas plus de 5 minutes par semaine à perdre avec vous et qui continuent malgré tout à obtenir un design minimaliste nécessite un excellent développeur de logiciels. Astuce: le développement de logiciels ne se limite pas à des compétences en matière de codage: il s’agit de négociation, de conversation et de prise de possession Tu dois être un chien Alpha et mordre dans le dos parfois.
maple_shaft

1
Si je comprends bien, cette réponse pose la question.
Konrad Rudolph

1
@maple_shaft Je pense que c'est ce que Jeff O voulait dire avec son commentaire, non?
ZweiBlumen

10

Je «écris» tous les tests auxquels je peux penser dès le départ en «assaillant», mais j’écris chaque test sous la forme d’un commentaire décrivant celui-ci.

Je convertis ensuite un test en code et effectue le travail pour qu'il se compile et passe . Souvent, je décide que je n'ai pas besoin de tous les tests que je pensais avoir, ou que j'ai besoin de tests différents, cette information provient uniquement de l'écriture du code permettant de réussir les tests.

Le problème est que vous ne pouvez pas écrire de test dans le code tant que vous n'avez pas créé la méthode et les classes qu'il teste. Sinon, vous obtiendrez beaucoup d'erreurs du compilateur qui vous empêcheraient de travailler sur un seul test à la fois.

Maintenant, si vous utilisez un système tel que spec flow lorsque les tests sont écrits en «anglais», vous voudrez peut-être que les clients acceptent un ensemble de tests tant que vous en avez le temps, plutôt que de créer un seul test.


1
Oui, même si je suis d'accord avec les réponses ci-dessus qui soulignent les problèmes de codage de tous vos tests en premier lieu, je trouve très utile de vider ma compréhension globale de la façon dont la méthode actuelle devrait se comporter comme un ensemble de descriptions de test sans code. Le processus d’écriture tend à préciser si j’ai bien compris ce qu’il faut du code que je vais écrire et s'il existe des cas extrêmes auxquels je n’ai pas pensé. Je me trouve beaucoup plus à l'aise pour coder le premier test et le faire passer après avoir exposé mon «aperçu général» du fonctionnement de la méthode.
Mark Weston

10

L'idée derrière TDD est des itérations rapides.

Si vous avez un grand nombre de tests à écrire avant d'écrire votre code, il est difficile de refactoriser votre code de manière itérative.

Sans une refactorisation facile du code, vous perdez beaucoup des avantages de TDD.


5

Grâce à mon expérience (limitée) avec le TDD, je peux vous dire que chaque fois que j'ai enfreint la discipline consistant à écrire un test à la fois, les choses se sont mal passées. C'est un piège facile à tomber. "Oh, cette méthode est triviale", vous vous dites, "alors je vais assommer ces deux autres tests et continuer à avancer." Bien devinez quoi? Rien n'est aussi trivial qu'il n'y paraît. Chaque fois que je suis tombé dans ce piège, je finissais par déboguer quelque chose que je trouvais facile, mais qui comportait des cas étranges. Et depuis que j'avais écrit plusieurs tests à la fois, c’était beaucoup de travail de localiser le bogue.

Si vous avez besoin d'une masse d'informations, vous avez beaucoup d'options:

  • Tableau blanc
  • Histoires d'utilisateurs
  • commentaires
  • Bon vieux stylo et papier

Notez que le compilateur ne figure nulle part sur cette liste. :-)


5

Vous supposez que vous savez à quoi ressemblera votre code avant de l'écrire. TDD / BDD est autant un processus de conception / découverte qu’un processus d’assurance qualité. Pour une fonctionnalité donnée, vous écrivez le test le plus simple permettant de vérifier que la fonctionnalité est satisfaite (cela peut parfois nécessiter plusieurs tests en raison de la complexité d'une fonctionnalité). Le premier test que vous écrivez est chargé avec des hypothèses sur l’aspect du code de travail. Si vous écrivez l'intégralité de la suite de tests avant d'écrire la première ligne de code pour la prendre en charge, vous formulez une litanie d'hypothèses non vérifiées. Au lieu de cela, écrivez une hypothèse et vérifiez-la. Puis écris le suivant. Lors du processus de vérification de la prochaine hypothèse, vous pouvez simplement casser une hypothèse antérieure. Vous devez donc revenir en arrière et changer cette première hypothèse pour qu'elle corresponde à la réalité ou la changer pour que la première hypothèse s'applique toujours.

Pensez à chaque test unitaire que vous écrivez comme une théorie dans un cahier scientifique. En remplissant le cahier, vous confirmez vos théories et en formez de nouvelles. Parfois, prouver une nouvelle théorie contredit une théorie antérieure, il faut donc la corriger. Il est plus facile de prouver une théorie à la fois plutôt que d'essayer de prouver 20 à la fois.


TDD suppose que vous sachiez à quoi ressemblera votre code avant de l'écrire, mais uniquement en morceaux plus petits.
Michael Shaw

4

TDD est une approche hautement itérative, qui (selon mon expérience) correspond mieux aux modes de développement du monde réel. Habituellement, ma mise en œuvre prend forme progressivement au cours de ce processus, et chaque étape peut amener de nouvelles questions, idées et idées à tester. Ceci est idéal pour garder mon esprit concentré sur la tâche réelle et est très efficace car je n'ai besoin que de garder un nombre limité de choses dans la mémoire à court terme à tout moment. Cela réduit la possibilité d'erreurs.

Votre idée est fondamentalement une approche Big Test Up Front, qui est plus difficile à gérer à mon humble avis et peut devenir une source de gaspillage. Et si vous réalisiez à mi-parcours de votre travail que votre approche n’est pas bonne, que votre API est défectueuse et que vous devez tout recommencer ou utiliser une bibliothèque tierce à la place? Ensuite, une grande partie du travail consacré à la rédaction de vos tests devient un effort inutile.

Cela dit, si cela fonctionne pour vous, bien. J'imagine que si vous travaillez à partir d'une spécification technique précise et détaillée, dans un domaine dans lequel vous êtes intimement familiarisé et / ou dans une tâche relativement petite, vous pouvez disposer de la plupart ou de la totalité des scénarios de test nécessaires et clarifier votre mise en œuvre dès la le début. Ensuite, il pourrait être judicieux de commencer par écrire tous les tests à la fois. Si votre expérience montre que cela vous rend plus productif à long terme, vous n'avez pas à vous soucier de la réglementation :-)


4

Au-delà du simple fait de penser à une chose, l'un des paradigmes de TDD consiste à écrire le moins de code possible pour réussir le test. Lorsque vous écrivez un test à la fois, il est beaucoup plus facile de savoir comment écrire suffisamment de code pour que ce test réussisse. Avec toute une série de tests à réussir, vous n’allez pas au code par petites étapes, mais vous devez faire un grand saut pour les faire passer en une fois.

Maintenant, si vous ne vous limitez pas à écrire le code pour les faire passer tous «d'un coup», mais écrivez plutôt juste assez de code pour passer un test à la fois, cela pourrait quand même fonctionner. Cependant, il vous faudrait plus de discipline pour ne pas écrire et écrire plus de code que nécessaire. Une fois que vous commencez dans cette voie, vous vous permettez d'écrire plus de code que ne le décrivent les tests, ce qui peut ne pas être testé , du moins dans le sens où il n'est pas piloté par un test et peut-être même dans le sens où il n'est pas nécessaire. (ou exercé) par tout test.

Décrire ce que la méthode devrait faire, sous forme de commentaires, d'histoires, de spécifications fonctionnelles, etc., est parfaitement acceptable. J'attendrais cependant de les traduire en tests un à la fois.

L'autre chose que vous pouvez manquer en écrivant les tests en une seule fois est le processus de réflexion selon lequel le passage d'un test peut vous inciter à penser à d'autres cas de test. Sans une banque de tests existants, vous devez penser au prochain cas de test dans le contexte du dernier test de réussite. Comme je l'ai dit, avoir une bonne idée de ce que la méthode est censée faire est très bon, mais plusieurs fois je me suis trouvé à trouver de nouvelles possibilités que je n'avais pas envisagées à priori, mais qui ne se sont produites qu'au cours de l'écriture du texte. tests. Vous risquez de ne pas les voir, à moins que vous ne preniez l'habitude de penser aux nouveaux tests que je peux écrire et que je n'ai pas encore.


3

J'ai travaillé sur un projet dans lequel les développeurs qui ont écrit les tests (qui échouaient) étaient différents de ceux qui implémentaient le code nécessaire pour les faire passer et je l'ai trouvé vraiment efficace.

Dans ce cas, seuls les tests liés à l'itération actuelle ont été écrits une fois. Donc, ce que vous suggérez est parfaitement possible dans ce genre de scénario.


2
  • Ensuite, vous essayez de vous concentrer sur trop de choses à la fois.
  • Lors de la mise en œuvre de tous les tests, vous n’avez pas de version de travail de votre application. Si vous devez en implémenter beaucoup, vous n'aurez pas de version de travail sur une longue période.

2

Le cycle Red-Green-Refactor est une liste de contrôle destinée aux développeurs novices en TDD. Je dirais que c'est une bonne idée de suivre cette liste de contrôle jusqu'à ce que vous sachiez quand et où vous pouvez la suivre (c'est-à-dire, jusqu'à ce que vous ne soyez pas obligé de poser cette question sur stackoverflow :)

Ayant suivi TDD pendant près de 10 ans, je peux vous affirmer que j’écris rarement, voire jamais, de nombreux tests avant d’écrire du code de production.


1

Vous décrivez BDD, où une partie prenante externe a une spécification exécutable. Cela peut être avantageux s'il existe une spécification initiale prédéterminée (par exemple, une spécification de format, une norme industrielle ou si le programmeur n'est pas l'expert du domaine).

L’approche normale consiste alors à couvrir progressivement de plus en plus de tests d’acceptation, ce qui correspond à la progression visible du chef de projet et du client.

Vous avez généralement ces tests spécifiés et exécutés dans un framework BDD tel que Cucumber, Fitnesse ou autre.

Cependant, ce n'est pas quelque chose que vous mélangez avec vos tests unitaires, qui sont beaucoup plus proches des détails de mise en œuvre de Nitty Gritty avec beaucoup de cas de pointe liées à l'API, les problèmes d'initialisation etc fortement concentrés sur l' objet sous test , qui est un artefact de mise en œuvre .

La discipline rouge-vert-refactor comporte de nombreux avantages, et le seul avantage que vous pouvez espérer en les tapant à l’avant est d’atteindre le seuil de rentabilité.


1

Un test à la fois: le principal avantage est de se concentrer sur une chose. Pensez à la conception qui privilégie la profondeur: vous pouvez aller en profondeur et rester concentré avec une boucle de rétroaction rapide. Vous risquez cependant de manquer l'étendue du problème! C'est le moment (le grand) refactoring entre en jeu. Sans cela, le TDD ne fonctionne pas.

Tous les tests: l'analyse et la conception peuvent vous révéler plus de la portée du problème. Pensez à la conception d'abord large. Vous analysez le problème sous plusieurs angles et ajoutez des données d'expérience. C'est intrinsèquement plus difficile, mais peut générer des avantages intéressants - moins de refactorisation - si vous en faites «juste assez». Méfiez-vous, il est facile de sur-analyser et de rater complètement la cible!

Je trouve difficile de recommander généralement de préférer l'un ou l'autre, car les facteurs sont nombreux: expérience (surtout avec le même problème), connaissances et compétences du domaine, convivialité du code de refactorisation, complexité du problème ...

J'imagine que si nous nous focalisions plus étroitement sur les applications métier classiques, alors TDD avec son approche de test rapide et d'erreur la plus rapide en terme d'efficacité serait de gagner.


1

En supposant que votre infrastructure de test le supporte, je suggérerais qu'au lieu d' implémenter les tests que vous voulez braindump, écrivez plutôt des tests descriptifs en attente que vous implémenterez plus tard. Par exemple, si votre API doit faire foo and bar mais pas biz, ajoutez simplement le code suivant (cet exemple est en rspec) pour votre suite de tests, puis attaquez-les un par un. Vous obtenez vos idées rapidement et pouvez résoudre tous vos problèmes, un par un. Lorsque tous les tests seront réussis, vous saurez quand vous aurez résolu tous les problèmes que vous avez rencontrés pendant votre braindump.

describe "Your API" do

  it "should foo" do
    pending "braindump from 4/2"
  end

  it "should bar" do
    pending "braindump from 4/2"
  end

  it "should not biz" do
    pending "braindump from 4/2"
  end

end
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.