Quels sont les inconvénients de la programmation test-first?

47

Il fait fureur de nos jours. "Tout le monde" le recommande. Cela en soi me rend méfiant.

Quels sont les inconvénients rencontrés lors du développement test-first (piloté par test)? Je recherche des expériences personnelles auprès de praticiens compétents - je peux lire les rêveries hypothétiques de cent aspirants ailleurs sur Internet.

Je demande non pas parce que je cherche à haïr le TDD, mais parce que mon travail consiste à améliorer le processus de développement de logiciels. Plus nous pourrons en apprendre davantage sur les problèmes rencontrés par les gens, plus nous aurons de chances d’améliorer le processus.

Alex Feinman
la source

Réponses:

41

Il y en a plusieurs, mais les avantages dépassent de loin les inconvénients.

Il y a une courbe d'apprentissage abrupte.

De nombreux développeurs semblent s’attendre à ce qu’ils puissent être efficaces avec une programmation d’essai d’abord dès le premier jour. Malheureusement, il faut beaucoup de temps pour acquérir de l'expérience et programmer à la même vitesse qu'auparavant. Vous ne pouvez pas vous en sortir.

Pour être plus précis, il est très facile de se tromper. Vous pouvez très facilement (avec de très bonnes intentions) finir par écrire toute une série de tests difficiles à maintenir ou à tester. Il est difficile de donner des exemples ici - ce genre de problèmes demande simplement de l'expérience à résoudre. Vous devez avoir une bonne idée de la séparation des préoccupations et de la conception pour la testabilité. Mon meilleur conseil ici serait de faire de la programmation en binôme avec quelqu'un qui connaît très bien le TDD.

Vous faites plus de codage à l'avant.

Test-first signifie que vous ne pouvez pas ignorer les tests (ce qui est bien) et que vous finirez par écrire plus de code tout de suite. Cela signifie plus de temps. Encore une fois, vous ne pouvez pas vous en sortir. Vous êtes récompensé par un code plus facile à maintenir, à étendre et généralement moins de bugs, mais cela prend du temps.

Peut être difficile à vendre aux gestionnaires.

Les gestionnaires de logiciels ne s’occupent généralement que des délais Si vous passez à la programmation test-first et que vous mettez soudainement deux semaines à compléter une fonctionnalité au lieu d'une, cela ne leur plaira pas. C'est certainement une bataille qui vaut la peine d'être menée et de nombreux gestionnaires sont suffisamment éclairés pour l'obtenir, mais cela peut être difficile.

Peut être difficile à vendre aux autres développeurs.

Étant donné que la courbe d'apprentissage est abrupte, tous les développeurs n'aiment pas la programmation test-first. En fait, je suppose que la plupart des développeurs ne l'aiment pas au début. Vous pouvez faire des choses comme la programmation en paire pour les aider à devenir rapides, mais cela peut être difficile à vendre.

En fin de compte, les avantages l'emportent sur les inconvénients, mais cela n'aide pas si vous ignorez simplement les inconvénients. En sachant dès le départ avec quoi vous traitez, vous aurez la possibilité de surmonter certains, voire la totalité des inconvénients.

Jaco Pretorius
la source
Ce sont de bonnes réponses, mais pourraient être plus spécifiques sur le n ° 1? Je suis particulièrement intéressé à savoir comment / si vous avez pu retrouver votre vitesse de programmation - qu'avez-vous appris que vous ne saviez pas quand vous avez commencé à faire du TDD?
Alex Feinman
Mis à jour pour donner des éclaircissements
Jaco Pretorius
7
Si vous testez maintenant, le temps total consacré au développement ne devrait pas changer de manière significative. Il semble que les choses prennent plus de temps parce que vous exposez le temps nécessaire pour écrire et gérer les tests unitaires.
ChrisF
1
@JeffO connaissez-vous le "Je vais m'écrire une mini-fourgonnette!" école de codage?
Alex Feinman
1
@tvanfosson - parce qu'ils essaient de changer deux choses à la fois - le fait de commencer les tests et le TDD - ce qui peut être problématique. Cela ajoute également aux estimations de temps - assez correctement - de sorte que les gestionnaires et les clients ne voient que l'augmentation initiale, sans que le temps total soit réellement connu (pour une fois) et peut même être inférieur. S'ils font des tests, cette augmentation sera moindre.
ChrisF
35

Test-first suppose que vous écrivez du code qui est:

  • testable de manière unitaire
  • que ce que vous développez a une approche évidente et n'exige pas de prototypage ou d'expérimentation poussé
  • que vous n'aurez pas besoin de refactoriser trop lourdement ou que vous avez le temps de réécrire à plusieurs reprises des centaines, voire des milliers de scénarios de test
  • rien n'est scellé
  • tout est modulaire
  • tout est injectable ou mockable
  • que votre organisation accorde une valeur suffisamment élevée aux défauts faibles pour justifier le puits de ressources
  • qu'il y a quelque chose d'intéressant à tester au niveau des tests unitaires

Si votre projet ne répond pas à ces exigences, vous aurez des difficultés. Les promoteurs de TDD n’ont pas de bonnes réponses à cet autre pour vous suggérer de revoir la conception de votre produit afin de mieux répondre à ces critères. Il y a des situations où cela est impossible ou indésirable.

Dans la pratique, il peut également être très problématique pour les personnes qui pensent que les tests d’abord-test prouvent réellement quelque chose au sujet du fonctionnement correct du programme. Dans de nombreux cas, cela n’est pas vrai, mais même dans les cas où c’est vrai, cela est loin d’être une image complète de la décision correcte. Les gens voient des centaines de tests réussis et supposent qu'il est prudent de faire moins de tests puisqu'avant le TDD, ils ne faisaient de toute façon que quelques centaines de tests. D'après mon expérience, TDD signifie que vous devez avoir encore plus de tests d'intégration, car les développeurs disposeront également d'une fausse sécurité. De plus, la difficulté de modifier tous les tests pour créer un gros rédacteur peut amener les développeurs à trouver des solutions intéressantes.

Exemples:

Mon meilleur exemple personnel est lors de la rédaction du code de sécurité pour asp.net. S'ils sont conçus pour fonctionner dans un environnement hostile à partir de la configuration de la machine, ils sont envoyés, signés et scellés. Parce qu'ils fonctionnent contre des objets divins d'IIS, ils sont très difficiles à imiter correctement. Ajoutez quelques contraintes en termes de performances et d’utilisation de la mémoire et vous perdez très rapidement la flexibilité nécessaire pour utiliser des objets fictifs dans les zones restantes.

N'importe quel type de microcontrôleur ou autre code d'environnement à faibles ressources peut ne pas être possible de faire une véritable conception de style OO car les abstractions ne sont pas optimisées et que les ressources sont limitées. La même chose peut être dite pour les routines hautes performances dans de nombreux cas également.

Facture
la source
Pouvez-vous donner des contre-exemples? Quand est-ce que je n'écrirais pas quelque chose qui est testable en test unitaire? Pourquoi n'écrirais-je pas un code moqueur ou injectable (autre que le code hérité, qui est un sujet en soi)?
Alex Feinman
édité pour ajouter une section d'exemples
Bill
4
D'accord. Le travail de TDD semble reposer sur un ensemble d’hypothèses concernant les machines avec lesquelles vous travaillez; cela ne semble pas être vrai pour environ 50% de mes projets.
Paul Nathan
Je suis totalement d'accord ... Excellente réponse
Khelben le
2
C'est comme n'importe quoi dans ce jeu - approprié pour de nombreuses situations, inapproprié pour d'autres. Méfiez-vous des personnes qui préconisent une voie unique dans tous les domaines du développement de logiciels.
Alan B
25

Le plus gros inconvénient que j'ai vu n'est pas avec le TDD lui-même mais avec les praticiens. Ils adoptent une approche dogmatique et zélée où tout doit être testé . Parfois (ce qui est souvent le cas), cela n'est pas nécessaire. En outre, cela pourrait ne pas être pratique (par exemple, présenter une organisation à TDD.)

Un bon ingénieur trouve des compromis et applique le bon équilibre entre quand / où / comment appliquer le test d’abord. En outre, si vous passez constamment plus de temps à développer des tests plutôt que du code réel (par un facteur de 2 ou plus), vous êtes en difficulté.

En d'autres termes, soyez pragmatique et raisonnable avec TDD (ou n'importe quoi dans le développement de logiciel d'ailleurs).

luis.espinal
la source
Est-ce que c'est peut-être de cela que provient la "nouvelle" définition du code hérité ("Code sans tests") de Michael Feathers?
Phill W.
Cette définition ne fonctionnerait pas pour moi :) Pour moi, tout code qui est en production et qui fait l'objet de modifications est un code hérité, indépendamment du code ou de la qualité du test. Nous associons généralement "code hérité" à "code incorrect" ou "code obsolète" alors qu'en réalité, un code incorrect et un code obsolète sont déjà présents dans le code en cours de développement qui n'a pas encore été utilisé en production. Notre objectif devrait être que notre code soit un héritage dès le départ et qu'il soit d'une qualité et d'une utilité telles qu'il reste en usage pendant des années, voire des décennies.
luis.espinal
6

J'ai commencé à travailler chez TDD au début d'août 2009 et j'ai convaincu toute mon entreprise de le remplacer en septembre / octobre 2009. À l'heure actuelle, toute l'équipe de développeurs est entièrement convertie et l'envoi de code non testé dans le référentiel est considéré comme une mauvaise chose. Cela a très bien fonctionné pour nous et je ne peux pas imaginer revenir au codage des cow-boys.

Cependant, il y a deux problèmes assez remarquables.

La suite de tests doit être maintenue

Lorsque vous êtes sérieux au sujet de TDD, vous finirez par écrire de nombreux tests. De plus, il faut du temps et de l'expérience pour comprendre quelle est la bonne granularité des tests (exagérer est presque aussi grave que de le faire). Ces tests sont aussi du code , et ils sont susceptibles à bitrot. Cela signifie que vous devez les maintenir comme tout le reste: mettez-le à jour lorsque vous mettez à niveau des bibliothèques dont ils dépendent, refactorisez de temps en temps ... Lorsque vous apportez de grandes modifications à votre code, de nombreux tests deviennent soudainement obsolètes ou obsolètes. même tout simplement faux. Si vous êtes chanceux, vous pouvez simplement les supprimer, mais souvent vous finirez par extraire les bits utiles et les adapter à la nouvelle architecture.

Test des abstractions qui fuient de temps en temps

Nous utilisons Django, qui dispose d’un très bon framework de tests. Cependant, il fait parfois des hypothèses légèrement en contradiction avec la réalité. Par exemple, certains middlewares peuvent casser les tests. Ou encore, certains tests supposent un backend de mise en cache. De même, si vous utilisez une "vraie" base de données (pas SQLite3), la préparation de la base de données pour les tests prendra beaucoup de temps. Bien sûr, vous pouvez (et devriez) utiliser SQLite3 et une base de données en mémoire pour les tests que vous effectuez localement, mais certains codes se comporteront différemment en fonction de la base de données utilisée. La configuration d’un serveur d’intégration continue fonctionnant de manière réaliste est indispensable.

(Certaines personnes vous diront que vous devriez vous moquer de tout ce qui est comme la base de données, sinon vos tests ne sont pas "purs", mais ce n'est que de l'idéologie. Si vous faites des erreurs dans votre code moqueur (et croyez-moi, vous le ferez), votre suite de tests sera sans valeur.)

Ceci étant dit, les problèmes que je décris ne commencent à être visibles que lorsque vous êtes assez avancé avec TDD ... Lorsque vous commencez tout juste avec TDD (ou que vous travaillez sur des projets plus petits), le refactoring de test ne sera pas un problème.

Ryszard Szopa
la source
3
+1 "doit être maintenu": le test du code réutilisable pose bien moins de problèmes, car son interface et son comportement doivent normalement être stables. Pour cette raison, je ne fais normalement que des TDD pour notre bibliothèque réutilisable.
Dimitri C.
4

Pour moi, il y a un problème psychologique profond avec les tests chaque fois que j'essaie de les appliquer de manière intensive, comme dans TDD: s'ils sont présents, je code mal, car je suis convaincu que les tests détecteront tous les problèmes. Mais s'il n'y a pas de tests pour fournir un filet de sécurité, je code soigneusement et le résultat est invariablement meilleur que celui obtenu avec les tests.

Peut-être que c'est juste moi. Mais j'ai aussi lu quelque part que les voitures avec toutes sortes de cloches et de sifflets de sécurité ont tendance à se bloquer davantage (parce que les conducteurs savent que les dispositifs de sécurité sont là), alors c'est peut-être quelque chose à reconnaître; Le TDD peut être incompatible avec certaines personnes.

Joonas Pulakka
la source
Cela me semble étrange, car écrire du code testable me fait généralement ralentir et réfléchir davantage à ce que je code. En fait, je suis un peu nerveux à coder sans tests ces jours-ci.
Matt H
1
Cela montre simplement que différentes personnes réagissent en effet différemment. Je ne dénigre pas le TDD - il est évident que de nombreuses personnes le trouvent utile - mais le fait est que ce n'est pas pour tout le monde.
Joonas Pulakka
2
Je suis d'accord à 100%. J'écris du code mieux et plus rapidement sans tests automatisés. Bien sûr, il serait absurde de ne pas tester, je pense juste que l’automatisation est un mauvais choix (du moins pour moi). Je trouve que les tests manuels sont à la fois plus rapides que de maintenir une suite de tests et plus sûrs - mais je suis aussi un développeur expérimenté, je suis donc très doué pour savoir quoi tester et où et pourquoi, alors mes ajouts et re-facteurs de code sont sans régression.
Ben Lee
1
Bien que je devrais souligner que l’équipe avec laquelle je travaille et les projets sont suffisamment petits pour que je comprenne bien l’ensemble de l’architecture - sur une grande équipe ou sur un très grand projet, je pourrais penser que les tests automatisés sont plus utiles, car alors aucun développeur ne serait nécessairement capable de sentir où ils devraient être testés pour éviter les régressions.
Ben Lee
Vous oubliez l'étape de refactoring?
rjnilsson
2

Une situation dans laquelle le test d’abord me gêne vraiment est lorsque je veux essayer rapidement une idée et voir si cela peut fonctionner avant d’écrire une implémentation appropriée.

Mon approche est normalement:

  1. Mettre en œuvre quelque chose qui fonctionne (preuve de concept).
  2. Si cela fonctionne, consolidez-le en ajoutant des tests, en améliorant la conception, en refactoring.

Parfois, je ne parviens pas à l'étape 2.

Dans ce cas, utiliser TDD s'est avéré avoir plus d'inconvénients que d'avantages pour moi:

  • Ecrire des tests lors de la mise en œuvre de la preuve de concept me ralentit et interrompt mon flux de pensées: je veux comprendre une idée et je ne veux pas perdre de temps à tester les détails de ma première mise en œuvre approximative.
  • Cela peut prendre plus de temps pour savoir si mon idée vaut quelque chose ou non.
  • S'il s'avère que l'idée était inutile, je dois jeter à la fois mon code et mes tests unitaires bien écrits.

Ainsi, lorsque je dois explorer de nouvelles idées, je n’utilise pas TDD et ne présente des tests unitaires que lorsque j’ai le sentiment que le nouveau code va quelque part.

Giorgio
la source
1
On dirait que vous confondez un code prototype avec un code utilisable. Le code prototype est le code de test . Il n’a pas besoin d’être testé et vous ne devez pas créer de tests qui en découlent. L'étape qui vous manque est comprise entre 1 et 2 .: vous dites "consolider en écrivant des tests". Le problème est que vous n'avez pas quelque chose à consolider, mais quelque chose à écrire. Prévoyez de réécrire le code prototype, ne prévoyez pas de le réutiliser. La réutiliser laisse beaucoup de place au compromis. La réécriture formalise la scission entre votre phase d'exploration et votre phase "code de qualité".
utnapistim
3
@utnapistim: Je ne confonds pas le code prototype avec le code utilisable, mais les fanatiques de TDD les confondent et suggèrent que vous utilisiez également TDD pour le code prototype. Ou plutôt, ils supposent qu'il n'y a pas de code prototype du tout. De plus, je conviens avec vous que vous devez souvent réécrire lorsque vous passez du prototype à la mise en œuvre réelle. Parfois, vous pouvez réutiliser des parties du code prototype, mais vous devez être prêt à réécrire. Vous devez vraiment décider au cas par cas.
Giorgio
3
@utnapistim: Voir aussi la réponse de luis.espinal: "Le plus gros inconvénient que j'ai constaté n'est pas avec TDD, mais avec les praticiens. Ils adoptent une approche dogmatique et zélée dans laquelle tout doit être testé.".
Giorgio
1

Inconvénients ou coûts du TDD

Remarque: il existe toute une gamme de types de TDD. Indépendamment de l’unité, du BDD, de l’ATDD ou d’autres variantes, de nombreuses difficultés demeurent.

Effets secondaires

Qu'il s'agisse de moqueries, de fixtures ou de tests fonctionnels, les dépendances sur des états ou des systèmes externes sont souvent à l'origine de la complexité des tests, de la confusion des méthodes de test et du risque de tromper. Quelques problèmes que j'ai vus:

  • Moqueur: oublie d'affirmer l'ordre des appels
  • Mocking: la maquette ne correspond pas à un appel ou à une réponse réelle
  • Fixture: le test repose sur des données irréalistes, masquant d'autres problèmes
  • Fixture: teste un état impossible en production
  • Fonctionnel: la construction est fausse car le système dépendant est temporairement indisponible
  • Fonctionnel: la vitesse de test est très lente

Vous devrez changer votre approche en matière de codage. Pour certains, ce sera un changement radical.

Différentes personnes codent de manière très différente. Dans TDD, vous devez commencer par un test qui affirme un comportement spécifique, puis implémenter pour que le test réussisse. J'ai vu et était un programmeur dont la programmation n'était pas propice au TDD. Cela m'a pris environ 2 mois lorsque j'ai commencé à m'habituer à changer mon approche de développement.

Il faut du temps pour comprendre ce qui compte pour vous et ce qui ne vous intéresse pas.

Chaque équipe doit prendre une décision explicite sur les endroits où elle souhaite tracer la ligne de démarcation. Quelles sont les choses qu'ils apprécient qu'ils veulent tester et ce qu'ils ne font pas. C'est souvent un processus douloureux d'apprendre à rédiger de bons tests et ce que vous vous souciez réellement de tester. En attendant, le code continuera à évoluer jusqu'à ce que le style et l'approche soient cohérents.

Test unitaire spécifique: grands refactors

Un refactor important ou fondamental d'une base de code significative avec des dizaines de milliers de tests unitaires générera un coût énorme pour la mise à jour de tous les tests. Cela se traduira souvent par un refus de faire un refactor, même si c'est la bonne chose à faire, simplement en raison des coûts associés.

dietbuddha
la source
0

Mon analogie est les barrières sur une piste Scalextric. Si vous les mettez, vous êtes beaucoup moins prudent.

Les gens ont également un peu peur de leurs tests - car ils fonctionnent bien, ils croient que le code est entièrement testé alors que ce n’est que le début du processus de test.

À mon avis, TDD est un tremplin vers BDD. Une série de tests en cours n’aide pas vraiment les développeurs sans savoir ce qu’ils font. Avec BDD, la sortie de test est en anglais, ce qui documente les tests et renforce ainsi la compréhension du système.

Robbie Dee
la source
-1

Les avantages de TDD sont qu’il vous oblige à protéger votre code contre les personnes qui ne le comprennent pas. Oui, cela inclut souvent vous-même. Mais que se passe-t-il lorsque le code ne vaut pas la peine d'être gardé? Il y a beaucoup de code qui ne devrait même pas être là en premier lieu! Donc, le problème avec TDD, c’est quand il s’agit de développeurs qui écrivent du mauvais code. TDD ne les aidera probablement pas à écrire un bon code, il est beaucoup plus probable qu’ils écrivent d’horribles tests. Ainsi, dans leur cas, TDD ne fera qu'ajouter au désordre; Les tests mal écrits et / ou redondants ne sont pas plus amusants que les autres formes de mauvais code.

Johan
la source
1
Si vous-même ne comprenez pas votre propre code, comment une poignée parmi des milliards de tests peut-elle éventuellement éviter que le code soit erroné?
Michael Shaw
2
Parce que tu l'as compris quand tu l'as écrit mais que tu l'as oublié en cours de route?
Johan
+1 TDD ne protège pas contre un développeur qui a mal compris une exigence de l'entreprise. C'est là que BDD intervient ...
Robbie Dee