Qu'est-ce que je perds en adoptant une conception pilotée par les tests?
N'énumérer que les négatifs; ne mentionnez pas les avantages écrits sous une forme négative.
Qu'est-ce que je perds en adoptant une conception pilotée par les tests?
N'énumérer que les négatifs; ne mentionnez pas les avantages écrits sous une forme négative.
Réponses:
Plusieurs inconvénients (et je ne prétends pas qu'il n'y a aucun avantage - en particulier lors de l'écriture des fondations d'un projet - cela gagnerait beaucoup de temps à la fin):
Si vous voulez faire un "vrai" TDD (lire: tester d'abord avec les étapes de refactorisation rouge, vert), alors vous devez également commencer à utiliser des mocks / stubs, lorsque vous voulez tester des points d'intégration.
Lorsque vous commencez à utiliser des simulations, après un certain temps, vous souhaiterez commencer à utiliser l'injection de dépendance (DI) et un conteneur d'inversion de contrôle (IoC). Pour ce faire, vous devez utiliser des interfaces pour tout (qui ont elles-mêmes de nombreux pièges).
À la fin de la journée, vous devez écrire beaucoup plus de code que si vous le faites simplement "à l'ancienne". Au lieu d'une simple classe client, vous devez également écrire une interface, une classe fictive, une configuration IoC et quelques tests.
Et n'oubliez pas que le code de test doit également être maintenu et entretenu. Les tests doivent être aussi lisibles que tout le reste et il faut du temps pour écrire du bon code.
De nombreux développeurs ne savent pas très bien comment faire tout cela "de la bonne façon". Mais parce que tout le monde leur dit que TDD est le seul véritable moyen de développer des logiciels, ils essaient juste du mieux qu'ils peuvent.
C'est beaucoup plus difficile qu'on pourrait le penser. Souvent, les projets réalisés avec TDD se retrouvent avec beaucoup de code que personne ne comprend vraiment. Les tests unitaires testent souvent la mauvaise chose, la mauvaise façon. Et personne ne convient à quoi devrait ressembler un bon test, pas même les soi-disant gourous.
Tous ces tests font qu'il est beaucoup plus difficile de «changer» (contrairement au refactoring) le comportement de votre système et les changements simples deviennent tout simplement trop difficiles et prennent beaucoup de temps.
Si vous lisez la littérature TDD, il y a toujours de très bons exemples, mais souvent dans les applications réelles, vous devez avoir une interface utilisateur et une base de données. C'est là que TDD devient vraiment difficile, et la plupart des sources n'offrent pas de bonnes réponses. Et s'ils le font, cela implique toujours plus d'abstractions: objets fictifs, programmation vers une interface, modèles MVC / MVP, etc., qui nécessitent encore beaucoup de connaissances, et ... vous devez écrire encore plus de code.
Alors soyez prudent ... si vous n'avez pas une équipe enthousiaste et au moins un développeur expérimenté qui sait écrire de bons tests et qui sait aussi quelques choses sur une bonne architecture, vous devez vraiment y réfléchir à deux fois avant de vous lancer sur la route TDD .
Lorsque vous arrivez au point où vous avez un grand nombre de tests, changer le système peut nécessiter une réécriture de tout ou partie de vos tests, selon ceux qui ont été invalidés par les changements. Cela pourrait transformer une modification relativement rapide en une modification très longue.
En outre, vous pourriez commencer à prendre des décisions de conception basées davantage sur TDD que sur des principes de conception réellement bons. Alors que vous aviez peut-être une solution très simple et facile qui est impossible de tester la façon dont TDD l'exige, vous avez maintenant un système beaucoup plus complexe qui est en fait plus sujet aux erreurs.
if part of the system is covered by tests and they pass, then everything is fine (including design)
.
Je pense que le plus gros problème pour moi est l'énorme perte de temps qu'il faut pour "y accéder". Je suis encore très au début de mon voyage avec TDD (voir mon blog pour les mises à jour de mes aventures de test si vous êtes intéressé) et j'ai littéralement passé des heures à commencer.
Il faut beaucoup de temps pour mettre votre cerveau en "mode test" et écrire du "code testable" est une compétence en soi.
TBH, je suis respectueusement en désaccord avec les commentaires de Jason Cohen sur la publication des méthodes privées, ce n'est pas de cela qu'il s'agit. Je n'ai pas rendu plus de méthodes publiques dans ma nouvelle façon de travailler qu'auparavant . Cependant, cela implique des modifications architecturales et vous permet de "hot plug" des modules de code pour rendre tout le reste plus facile à tester. Vous ne devez pas rendre les éléments internes de votre code plus accessibles pour ce faire. Sinon, nous sommes de retour à la case départ avec tout étant public, où est l'encapsulation dans tout cela?
Donc, (IMO) en bref:
PS: Si vous souhaitez des liens vers des points positifs, j'ai posé et répondu à plusieurs questions à ce sujet, consultez mon profil .
Depuis quelques années que je pratique le développement piloté par les tests, je dois dire que les plus gros inconvénients sont:
TDD se fait mieux par paires. D'une part, il est difficile de résister à l'envie d'écrire simplement l'implémentation lorsque vous SAVEZ comment écrire une instruction if / else . Mais une paire vous gardera sur la tâche parce que vous le gardez sur la tâche. Malheureusement, de nombreuses entreprises / dirigeants ne pensent pas que ce soit une bonne utilisation des ressources. Pourquoi payer pour que deux personnes écrivent une fonctionnalité, alors que j'ai deux fonctionnalités à faire en même temps?
Certaines personnes n'ont tout simplement pas la patience d'écrire des tests unitaires. Certains sont très fiers de leur travail. Ou, certains aiment juste voir des méthodes / fonctions alambiquées saigner à la fin de l'écran. TDD n'est pas pour tout le monde, mais je souhaite vraiment que ce soit le cas. Cela rendrait la maintenance des choses tellement plus facile pour les pauvres âmes qui héritent du code.
Idéalement, vos tests ne s'arrêteront que lorsque vous prendrez une mauvaise décision de code. Autrement dit, vous pensiez que le système fonctionnait dans un sens, et il s'est avéré que non. En cassant un test, ou un (petit) ensemble de tests, c'est en fait une bonne nouvelle. Vous savez exactement comment votre nouveau code affectera le système. Cependant, si vos tests sont mal écrits, étroitement couplés ou, pire encore, générés ( toux VS Test), le maintien de vos tests peut devenir rapidement une chorale. Et, après que suffisamment de tests commencent à générer plus de travail que la valeur perçue qu'ils créent, les tests seront la première chose à supprimer lorsque les plannings sont compressés (par exemple, il arrive à l'heure du resserrement)
Idéalement, encore une fois, si vous respectez la méthodologie, votre code sera testé à 100% par défaut. En règle générale, je pense que je me retrouve avec une couverture de code supérieure à 90%. Cela se produit généralement lorsque j'ai une architecture de style de modèle et que la base est testée et que j'essaie de couper les coins et de ne pas tester les personnalisations du modèle. De plus, j'ai découvert que lorsque je rencontre une nouvelle barrière que je n'avais pas rencontrée auparavant, j'ai une courbe d'apprentissage pour la tester. Je vais admettre avoir écrit quelques lignes de code à l'ancienne, mais j'aime vraiment avoir 100%. (Je suppose que j'étais plus performant à l'école, euh skool).
Cependant, avec cela, je dirais que les avantages du TDD l'emportent largement sur les négatifs pour l'idée simple que si vous pouvez réaliser un bon ensemble de tests qui couvrent votre application mais ne sont pas si fragiles qu'un changement les brise tous, vous le ferez pouvoir continuer à ajouter de nouvelles fonctionnalités le jour 300 de votre projet comme vous l'avez fait le jour 1. Cela ne se produit pas avec tous ceux qui essaient TDD en pensant que c'est une solution miracle à tout leur code bogue, et donc ils pensent que cela peut 't travail, point.
Personnellement, j'ai trouvé qu'avec TDD, j'écris du code plus simple, je passe moins de temps à débattre si une solution de code particulière fonctionnera ou non, et que je n'ai pas peur de changer une ligne de code qui ne répond pas aux critères énoncés par l'équipe.
Le TDD est une discipline difficile à maîtriser et j'y suis depuis quelques années et j'apprends toujours de nouvelles techniques de test. C'est un investissement de temps énorme à l'avance, mais, à long terme, votre durabilité sera beaucoup plus grande que si vous n'aviez pas de tests unitaires automatisés. Maintenant, si seulement mes patrons pouvaient comprendre cela.
Sur votre premier projet TDD, il y a deux grosses pertes, le temps et la liberté personnelle
Vous perdez du temps car:
Vous perdez votre liberté personnelle parce que:
J'espère que cela t'aides
TDD vous oblige à planifier le fonctionnement de vos classes avant d'écrire du code pour réussir ces tests. C'est à la fois un plus et un moins.
J'ai du mal à écrire des tests dans un "vide" - avant qu'un code n'ait été écrit. D'après mon expérience, j'ai tendance à trébucher sur mes tests chaque fois que je pense inévitablement à quelque chose en écrivant mes cours que j'ai oublié en écrivant mes tests initiaux. Il est alors temps non seulement de refactoriser mes cours, mais ÉGALEMENT mes tests. Répétez cette opération trois ou quatre fois et cela peut devenir frustrant.
Je préfère d'abord rédiger un brouillon de mes cours puis écrire (et maintenir) une batterie de tests unitaires. Après avoir un brouillon, TDD fonctionne bien pour moi. Par exemple, si un bogue est signalé, j'écrirai un test pour exploiter ce bogue, puis je corrigerai le code pour que le test réussisse.
Le prototypage peut être très difficile avec TDD - lorsque vous n'êtes pas sûr de la voie que vous allez prendre pour une solution, l'écriture des tests à l'avance peut être difficile (à part des très larges). Cela peut être pénible.
Honnêtement, je ne pense pas que pour le «développement de base» pour la grande majorité des projets, il y ait un réel inconvénient; il est beaucoup plus bas qu'il ne devrait l'être, généralement par des gens qui croient que leur code est assez bon pour qu'ils n'aient pas besoin de tests (ce n'est jamais le cas) et des gens qui ne peuvent tout simplement pas être dérangés de les écrire.
Eh bien, et cet étirement, vous devez déboguer vos tests. En outre, il y a un certain coût en temps pour écrire les tests, bien que la plupart des gens conviennent que c'est un investissement initial qui porte ses fruits sur la durée de vie de l'application, à la fois en termes de débogage et de stabilité.
Le plus gros problème que j'ai personnellement rencontré, cependant, est de prendre la discipline nécessaire pour passer les tests. Dans une équipe, en particulier une équipe établie, il peut être difficile de les convaincre que le temps passé en vaut la peine.
Si vos tests ne sont pas très approfondis, vous risquez de tomber dans un faux sentiment de «tout fonctionne» simplement parce que vos tests réussissent. Théoriquement, si vos tests réussissent, le code fonctionne; mais si nous pouvions écrire du code parfaitement la première fois, nous n'aurions pas besoin de tests. La morale ici est de vous assurer de faire un test de santé mentale par vous-même avant d'appeler quelque chose de complet, ne vous fiez pas uniquement aux tests.
Sur cette note, si votre vérification de la santé mentale trouve quelque chose qui n'est pas testé, assurez-vous de revenir en arrière et d'écrire un test pour cela.
L'inconvénient du TDD est qu'il est généralement étroitement associé à la méthodologie «Agile», qui ne importance à la documentation d'un système, mais la compréhension derrière pourquoi un test `` devrait '' renvoyer une valeur spécifique plutôt que toute autre réside uniquement dans le développeur. tête.
Dès que le développeur quitte ou oublie la raison pour laquelle le test renvoie une valeur spécifique et non une autre, vous êtes foutu. TDD est correct SI il est correctement documenté et entouré d'une documentation lisible par l'homme (c'est-à-dire un gestionnaire aux cheveux pointus) qui peut être consultée dans 5 ans lorsque le monde change et que votre application en a également besoin.
Quand je parle de documentation, ce n'est pas un texte de présentation, c'est une écriture officielle qui existe en dehors de l'application, comme des cas d'utilisation et des informations de base auxquelles peuvent se référer les gestionnaires, les avocats et la pauvre sève qui doit mettre à jour votre code en 2011.
J'ai rencontré plusieurs situations où TDD me rend fou. Pour en nommer:
Maintenabilité du scénario de test:
Si vous êtes dans une grande entreprise, il est fort probable que vous n'ayez pas à écrire les cas de test vous-même ou du moins la plupart d'entre eux sont écrits par quelqu'un d'autre lorsque vous entrez dans l'entreprise. Les fonctionnalités d'une application changent de temps en temps et si vous n'avez pas de système en place, tel que HP Quality Center, pour les suivre, vous deviendrez fou en un rien de temps.
Cela signifie également qu'il faudra beaucoup de temps aux nouveaux membres de l'équipe pour saisir ce qui se passe avec les cas de test. À son tour, cela peut se traduire par plus d'argent nécessaire.
Tester la complexité de l'automatisation:
Si vous automatisez une partie ou la totalité des cas de test dans des scripts de test exécutables par la machine, vous devrez vous assurer que ces scripts de test sont synchronisés avec leurs cas de test manuels correspondants et conformes aux modifications de l'application.
De plus, vous passerez du temps à déboguer les codes qui vous aident à détecter les bogues. À mon avis, la plupart de ces bogues proviennent de l'échec de l'équipe de test à refléter les changements d'application dans le script de test d'automatisation. Les changements dans la logique métier, l'interface graphique et d'autres éléments internes peuvent empêcher l'exécution de vos scripts ou leur exécution de manière non fiable. Parfois, les changements sont très subtils et difficiles à détecter. Une fois que tous mes scripts ont signalé un échec car ils ont basé leur calcul sur les informations du tableau 1 alors que le tableau 1 était maintenant le tableau 2 (car quelqu'un a échangé le nom des objets de la table dans le code d'application).
Le plus gros problème, ce sont les personnes qui ne savent pas comment écrire les tests unitaires appropriés. Ils écrivent des tests qui dépendent les uns des autres (et ils fonctionnent très bien avec Ant, mais échouent soudainement quand je les lance depuis Eclipse, juste parce qu'ils s'exécutent dans un ordre différent). Ils écrivent des tests qui ne testent rien en particulier - ils déboguent simplement le code, vérifient le résultat et le transforment en test, en l'appelant "test1". Ils élargissent le champ des classes et des méthodes, simplement parce qu'il sera plus facile de leur écrire des tests unitaires. Le code des tests unitaires est terrible, avec tous les problèmes de programmation classiques (couplage lourd, méthodes longues de 500 lignes, valeurs codées en dur, duplication de code) et est un enfer à maintenir. Pour une raison étrange, les gens traitent les tests unitaires comme quelque chose d’inférieur au «vrai» code, et ils t se soucient de leur qualité du tout. :-(
Vous perdez beaucoup de temps à passer des tests. Bien sûr, cela pourrait être enregistré à la fin du projet en attrapant les bogues plus rapidement.
Le plus gros inconvénient est que si vous voulez vraiment faire le TDD correctement, vous devrez beaucoup échouer avant de réussir. Étant donné le nombre de sociétés de logiciels qui fonctionnent (dollar par KLOC), vous finirez par être viré. Même si votre code est plus rapide, plus propre, plus facile à maintenir et contient moins de bogues.
Si vous travaillez dans une entreprise qui vous paie par les KLOC (ou les exigences mises en œuvre - même si elles ne sont pas testées), restez à l'écart du TDD (ou des révisions de code, ou de la programmation par paires, ou de l'intégration continue, etc., etc., etc.).
Vous perdez la possibilité de dire que vous avez «terminé» avant de tester tout votre code.
Vous perdez la possibilité d'écrire des centaines ou des milliers de lignes de code avant de l'exécuter.
Vous perdez l'occasion d'apprendre par le débogage.
Vous perdez la flexibilité d'expédier du code dont vous n'êtes pas sûr.
Vous perdez la liberté de coupler étroitement vos modules.
Vous perdez l'option d'ignorer l'écriture de documentation de conception de bas niveau.
Vous perdez la stabilité qui accompagne le code que tout le monde a peur de changer.
J'appuie la réponse sur le temps de développement initial. Vous perdez également la capacité de travailler confortablement sans la sécurité des tests. J'ai également été décrit comme un nutbar TDD, donc vous pourriez perdre quelques amis;)
Se recentrer sur des exigences difficiles et imprévues est le fléau constant du programmeur. Le développement piloté par les tests vous oblige à vous concentrer sur les exigences banales déjà connues et limite votre développement à ce qui a déjà été imaginé.
Pensez-y, vous finirez probablement par concevoir des cas de test spécifiques, donc vous ne serez pas créatif et ne commencerez pas à penser "ce serait cool si l'utilisateur pouvait faire X, Y et Z". Par conséquent, lorsque cet utilisateur commence à être enthousiasmé par les exigences potentielles cool X, Y et Z, votre conception peut être trop rigoureusement concentrée sur des cas de test déjà spécifiés, et il sera difficile à ajuster.
Ceci, bien sûr, est une épée à double tranchant. Si vous passez tout votre temps à concevoir pour chaque X imaginable, imaginable, X, Y et Z qu'un utilisateur puisse souhaiter, vous ne finirez inévitablement jamais rien. Si vous accomplissez quelque chose, il sera impossible pour quiconque (y compris vous-même) d'avoir une idée de ce que vous faites dans votre code / conception.
Il peut être difficile et long d'écrire des tests pour des données "aléatoires" comme les flux XML et les bases de données (pas si difficiles). J'ai récemment travaillé avec des flux de données météorologiques. C'est assez déroutant d'écrire des tests pour cela, du moins car je n'ai pas trop d'expérience avec TDD.
Vous perdrez de grandes classes avec de multiples responsabilités. Vous perdrez également probablement de grandes méthodes avec de multiples responsabilités. Vous pouvez perdre une certaine capacité de refactoriser, mais vous perdrez également une partie du besoin de refactoriser.
Jason Cohen a dit quelque chose comme: TDD nécessite une certaine organisation pour votre code. Cela pourrait être incorrect sur le plan architectural; par exemple, comme les méthodes privées ne peuvent pas être appelées en dehors d'une classe, vous devez rendre les méthodes non privées pour les rendre testables.
Je dis que cela indique une abstraction manquée - si le code privé doit vraiment être testé, il devrait probablement être dans une classe distincte.
Dave Mann
Vous devez écrire des applications d'une manière différente: une qui les rend testables. Vous seriez surpris de voir à quel point c'est difficile au début.
Certaines personnes trouvent le concept de penser à ce qu'elles vont écrire avant de l'écrire trop fort. Des concepts tels que la moquerie peuvent également être difficiles pour certains. TDD dans les applications héritées peut être très difficile si elles n'ont pas été conçues pour les tests. Le TDD autour de cadres qui ne sont pas compatibles avec le TDD peut également être difficile.
Le TDD est une compétence pour que les développeurs juniors puissent avoir du mal au début (principalement parce qu'ils n'ont pas appris à travailler de cette façon).
Dans l'ensemble, bien que les inconvénients soient résolus à mesure que les gens deviennent qualifiés et que vous finissez par résumer le code «malodorant» et avoir un système plus stable.
Cela prend un certain temps pour y entrer et un certain temps pour commencer à le faire dans un projet mais ... Je regrette toujours de ne pas avoir fait une approche Test Driven quand je trouve des bugs idiots qu'un test automatisé aurait pu trouver très rapidement. De plus, TDD améliore la qualité du code.
Bonnes réponses à tous. J'ajouterais quelques façons d'éviter le côté sombre du TDD:
J'ai écrit des applications pour faire leur propre auto-test aléatoire. Le problème avec l'écriture de tests spécifiques est que même si vous en écrivez beaucoup, ils ne couvrent que les cas auxquels vous pensez. Les générateurs de tests aléatoires trouvent des problèmes auxquels vous n'avez pas pensé.
Le concept de nombreux tests unitaires implique que vous avez des composants qui peuvent entrer dans des états invalides, comme des structures de données complexes. Si vous vous éloignez des structures de données complexes, il y a beaucoup moins à tester.
Dans la mesure où votre application le permet, soyez timide de conception qui repose sur le bon ordre des notifications, des événements et des effets secondaires. Ceux-ci peuvent facilement être abandonnés ou brouillés, ils ont donc besoin de beaucoup de tests.
TDD nécessite une certaine organisation pour votre code. Cela peut être inefficace ou difficile à lire. Ou même mal architecturalement; par exemple, puisqueprivate
méthodes ne peuvent pas être appelées en dehors d'une classe, vous devez rendre les méthodes non privées pour les rendre testables, ce qui est tout simplement faux.
Lorsque le code change, vous devez également modifier les tests. Avec la refactorisation, cela peut représenter beaucoup de travail supplémentaire.
Permettez-moi d'ajouter que si vous appliquez les principes BDD à un projet TDD, vous pouvez atténuer quelques-uns des principaux inconvénients énumérés ici (confusion, malentendus, etc.). Si vous n'êtes pas familier avec BDD, vous devriez lire l'introduction de Dan North. Il a proposé le concept en réponse à certains des problèmes posés par l'application du TDD sur le lieu de travail. L'introduction de Dan au BDD peut être trouvée ici .
Je ne fais cette suggestion que parce que BDD résout certains de ces points négatifs et agit comme un trou d'arrêt. Vous devrez en tenir compte lors de la collecte de vos commentaires.
Vous devez vous assurer que vos tests sont toujours à jour, le moment où vous commencez à ignorer les feux rouges est le moment où les tests deviennent vides de sens.
Vous devez également vous assurer que les tests sont complets, ou au moment où un gros bogue apparaît, le type de gestion étouffant que vous avez finalement convaincu de vous laisser passer du temps à écrire plus de code se plaindra.
La personne qui a enseigné le développement agile à mon équipe ne croyait pas à la planification, vous n'avez écrit que pour la moindre exigence.
Sa devise était refactor, refactor, refactor. J'ai compris que le refactorisme signifiait «ne pas planifier à l'avance».
Le temps de développement augmente: chaque méthode doit être testée et si vous avez une grande application avec des dépendances, vous devez préparer et nettoyer vos données pour les tests.