Qu'est-ce que je perds en adoptant une conception basée sur des tests?
N'énumérer que les négatifs; ne pas énumérer les avantages écrits sous une forme négative.
Plusieurs inconvénients (et je ne prétends pas qu’il n’ya aucun avantage - en particulier lors de la rédaction du projet - cela ferait gagner beaucoup de temps à la fin):
Si vous voulez faire de "vrais" TDD (lire: testez d’abord avec les étapes rouge, vert, refactorisation), vous devez également commencer à utiliser des mocks/stubs lorsque vous souhaitez tester les points d’intégration.
Lorsque vous commencerez à utiliser des simulacres, après un certain temps, vous voudrez commencer à utiliser l’injection de dépendance (DI) et un conteneur Inversion of Control (IoC). Pour ce faire, vous devez utiliser des interfaces pour tout (ce qui présente 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 rappelez-vous que le code de test doit également être maintenu et pris en charge. Les tests doivent être aussi lisibles que tout le reste et il faut du temps pour écrire du bon code.
Beaucoup de développeurs ne comprennent pas très bien comment faire tout cela "de la bonne façon". Mais comme tout le monde leur dit que TDD est le seul véritable moyen de développer des logiciels, ils essaient simplement 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, le mauvais sens. Et personne ne convient à quoi devrait ressembler un bon test, pas même les soi-disant gourous.
Tous ces tests rendent beaucoup plus difficile le changement de comportement de votre système et de simples changements deviennent 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 disposer d'une interface utilisateur et d'une base de données. C'est là que le TDD devient très difficile et que 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 pour une interface, modèles MVC/MVP, etc., qui exigent à nouveau 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 comment écrire de bons tests et qui connaît quelques notions de bonne architecture, vous devez vraiment y penser à deux fois avant de s'engager dans la voie du TDD. .
Lorsque vous atteignez le point où vous avez un grand nombre de tests, modifier le système peut nécessiter de réécrire tout ou partie de vos tests, en fonction de ceux qui ont été invalidés par les modifications. Cela pourrait transformer une modification relativement rapide en une opération très chronophage.
En outre, vous pouvez commencer à prendre des décisions de conception basées davantage sur le TDD que sur de bons principes de conception. Alors que vous aviez peut-être une solution très simple et facile, impossible à tester comme l'exige TDD, vous disposez maintenant d'un système beaucoup plus complexe qui est en réalité plus sujet aux erreurs.
Je pense que le plus gros problème pour moi est l'énorme perte de temps qu'il faut "pour y arriver". Je suis encore très au début de mon voyage avec TDD (Voir mon blog pour mettre à jour mes aventures de test si vous êtes intéressé) et j'ai littéralement passé heures pour commencer.
Il faut beaucoup de temps pour que votre cerveau passe 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 divulgation des méthodes privées, ce n’est pas de quoi il s’agit. Dans ma nouvelle méthode de travail, je n'ai pas utilisé plus de méthodes publiques qu'auparavant . Cela implique toutefois des modifications architecturales et vous permet de "brancher à chaud" des modules de code afin de rendre tout le reste plus facile à tester. Vous devriez ne 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 ce qui est public, où est l'encapsulation dans cela?
Donc, (IMO) en un mot:
PS: Si vous souhaitez des liens vers des éléments positifs, j’ai posé et répondu à plusieurs questions à ce sujet, consultez mon profil .
Au cours des quelques années où j'ai pratiqué le développement piloté par les tests, je dois dire que les plus gros inconvénients sont:
TDD est mieux fait par paires. D'une part, il est difficile de résister à l'envie d'écrire simplement l'implémentation quand vous SAVEZ comment écrire un si/else instruction. 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 cela représente une utilisation judicieuse des ressources. Pourquoi payer pour que deux personnes écrivent une fonction alors que deux fonctions doivent être réalisées en même temps?
Certaines personnes n'ont simplement pas la patience nécessaire pour écrire des tests unitaires. Certains sont très fiers de leur travail. Ou alors, certaines personnes aiment simplement voir des méthodes/fonctions compliquées se détacher de la fin de l'écran. Le TDD ne convient pas à tout le monde, mais j'aimerais vraiment qu'il en soit ainsi. Cela faciliterait tellement le travail de maintenance pour ces pauvres âmes qui héritent du code.
Idéalement, vos tests ne fonctionneront que lorsque vous prendrez une mauvaise décision de code. C'est-à-dire que vous pensiez que le système fonctionnait dans un sens et qu'il s'avère que ce n'est pas le cas. 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. Toutefois, si vos tests sont mal écrits, étroitement lié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 à causer plus de travail que la valeur perçue qu'ils créent, alors les tests seront la première chose à supprimer lorsque les planifications seront compressées (par exemple, le temps presse)
Idéalement, encore une fois, si vous adhérez à la méthodologie, votre code sera testé à 100% par défaut. En règle générale, je pense avoir 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. J'essaie de couper les angles et de ne pas tester les personnalisations du modèle. De plus, j'ai constaté que lorsque je rencontre un nouvel obstacle que je n'avais pas encore rencontré, je dois apprendre à le tester. Je vais admettre que certaines lignes de code ont été écrites à l'ancienne, mais j'aime beaucoup avoir ce code à 100%. (Je suppose que je réussissais trop bien à l'école, euh skool).
Cependant, avec cela, je dirais que les avantages de la TDD dépassent de loin les inconvénients de la simple idée que si vous pouvez réaliser une bonne série de tests couvrant votre demande mais ne sont pas si fragiles qu’un changement les casse tous, être capable de continuer à ajouter de nouvelles fonctionnalités au jour 300 de votre projet, comme vous l'avez fait au jour 1. Cela ne se produit pas pour tous ceux qui essaient de TDD en pensant que c'est une solution miracle pour tout leur code infesté de bogues, et ils pensent donc qu'il peut le faire. pas travailler, tout simplement.
Personnellement, j’ai constaté qu’avec TDD, j’écrivais du code plus simple, je passais moins de temps à débattre du point de savoir si une solution de code particulière fonctionnerait ou non, et que je ne craignais pas de changer une ligne de code qui ne répond pas aux critères définis par l'équipe.
Le TDD est une discipline difficile à maîtriser et je le pratique depuis quelques années, et j'apprends toujours de nouvelles techniques de test tout le temps. C'est un investissement de temps considérable au départ, 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 parce que:
Vous perdez votre liberté personnelle parce que:
J'espère que cela t'aides
TDD vous demande de planifier le fonctionnement de vos classes avant de pouvoir écrire du code pour réussir ces tests. C'est à la fois un avantage et un inconvénient.
Je trouve difficile d’écrire des tests dans un "vide" - avant qu’un code n’ait été écrit. Dans 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. Ensuite, il est temps non seulement de refactoriser mes cours, mais également de passer 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. Une fois que j'ai un brouillon, TDD fonctionne bien pour moi. Par exemple, si un bogue est signalé, je vais écrire un test pour exploiter ce bogue, puis corriger le code pour que le test réussisse.
Le prototypage peut être très difficile avec TDD - lorsque vous ne savez pas quel chemin vous allez emprunter pour trouver une solution, il peut s'avérer difficile de passer les tests à l’avance (autres que ceux très larges). Cela peut être une douleur.
Honnêtement, je ne pense pas que pour le "développement de base" de la grande majorité des projets, il y ait un réel inconvénient; les gens qui croient que leur code est suffisamment bon pour ne pas avoir besoin de tests (ce n'est jamais le cas) et qui ne sont simplement pas dérangés ne sont pas dérangés pour le faire.
Eh bien, et cet étirement, vous devez déboguer vos tests. En outre, il faut un certain temps pour écrire les tests, bien que la plupart des gens s'accordent pour dire qu'il s'agit d'un investissement initial rentable tout au long de la durée de vie de l'application, à la fois en termes de débogage rapide et de stabilité.
Le plus gros problème que j’ai eu personnellement avec lui, cependant, est d’avoir la discipline pour écrire 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.
L’inconvénient de TDD est qu’elle est généralement étroitement associée à la méthodologie 'Agile', qui accorde une importance nulle à la documentation d’un système, mais plutôt à la compréhension de la raison un test 'devrait' renvoyer une valeur spécifique plutôt qu'une autre réside uniquement dans la tête du développeur.
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 convient parfaitement si elle est correctement documentée et entourée d'une documentation lisible par l'homme (par exemple, un gestionnaire aux cheveux pointus), à laquelle on pourra se référer dans 5 ans lorsque le monde changera et que votre application doit également l'être.
Lorsque je parle de documentation, il ne s’agit pas d’un texte de référence en code, c’est une écriture officielle qui existe en dehors de l’application, telle que des cas d’utilisation et des informations générales pouvant être consultées par les gestionnaires, les avocats et le pauvre SAP qui doit mettre à jour votre code en 2011.
Si vos tests ne sont pas très approfondis, vous risquez de tomber dans un faux sentiment de "tout fonctionne" simplement parce que les 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 une vérification de votre santé mentale avant de qualifier quelque chose de complet, ne vous fiez pas uniquement aux tests.
Sur cette note, si votre vérification de santé vérifie quelque chose qui n’a pas été testé, assurez-vous de revenir en arrière et d’écrire un test.
J'ai rencontré plusieurs situations où TDD me rend fou. Pour en nommer quelques uns:
Maintenabilité du cas de test:
Si vous êtes dans une grande entreprise, il est fort probable que vous n’ayez pas à écrire les scénarios de test vous-même ou du moins que la plupart d’entre eux soient écrits par une autre personne lorsque vous entrez dans la société. 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 comprendre ce qui se passe avec les cas tests. À son tour, cela peut être traduit en plus d'argent nécessaire.
Complexité de l'automatisation des tests:
Si vous automatisez une partie ou la totalité des scénarios de test dans des scripts de test exécutables par une machine, vous devez vous assurer que ces scripts sont synchronisés avec les scénarios de test manuels correspondants et en adéquation avec les modifications apportées à l'application.
En outre, vous passerez du temps à déboguer les codes qui vous aident à détecter les bogues. À mon avis, la plupart de ces bugs proviennent de l'incapacité de l'équipe de test à refléter les modifications apportées à l'application dans le script de test d'automatisation. Les modifications apportées à la logique commerciale, à l'interface graphique et à d'autres éléments internes peuvent empêcher vos scripts de s'exécuter ou de les exécuter 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 avaient basé leur calcul sur les informations du tableau 1, tandis que le tableau 1 était désormais le tableau 2 (car une personne avait échangé le nom des objets de la table dans le code de l'application).
Le plus gros problème concerne les personnes qui ne savent pas é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 soudainement, ils échouent lorsque je les exécute depuis Eclipse, simplement parce qu'ils fonctionnent dans un ordre différent). Ils écrivent des tests qui ne testent rien en particulier: ils déboguent le code, vérifient le résultat et le modifient en test, en l'appelant "test1". Ils élargissent le champ d'application des classes et des méthodes, simplement parce qu'il sera plus facile d'écrire des tests unitaires pour elles. 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 des raisons étranges, les personnes considèrent les tests unitaires comme inférieurs aux "vrais" codes et ne se soucient pas du tout de leur qualité. :-(
Vous perdez beaucoup de temps à rédiger des tests. Bien sûr, cela pourrait être sauvegardé à la fin du projet en attrapant les bugs plus rapidement.
Vous perdez la possibilité de dire que vous avez "terminé" avant de tester tout votre code.
Vous perdez la capacité d'écrire des centaines ou des milliers de lignes de code avant de l'exécuter.
Vous perdez la possibilité d'apprendre par le débogage.
Vous perdez la flexibilité nécessaire pour 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 la rédaction d'une documentation de conception de bas niveau.
Vous perdez la stabilité associée au code que tout le monde a peur de changer.
Vous perdez le titre de "pirate informatique".
Le plus gros inconvénient est que si vous voulez vraiment faire correctement le TDD, vous devrez échouer beaucoup avant de réussir. Étant donné le nombre de sociétés de logiciels qui travaillent (dollar par KLOC), vous serez éventuellement viré. Même si votre code est plus rapide, plus propre, plus facile à gérer et comporte moins de bogues.
Si vous travaillez dans une entreprise qui vous paie en fonction des KLOC (ou des exigences mises en œuvre, même si elles n’ont pas été testées), restez à l’écart de la TDD (ou de la révision de code, de la programmation par paires, de l’intégration continue, etc., etc.).
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 mondaines déjà connues et limite votre développement à ce qui a déjà été imaginé.
Pensez-y, vous finirez probablement par concevoir des scénarios de test spécifiques, vous ne serez donc pas créatif et ne commencerez pas à penser "ce serait cool si l'utilisateur pouvait exécuter X, Y et Z". Par conséquent, lorsque cet utilisateur commence à s’enthousiasmer pour les exigences X, Y et Z potentielles, il est possible que votre conception soit trop rigide sur des scénarios de test déjà spécifiés et qu’il sera difficile de s’ajuster.
Ceci, bien sûr, est une épée à double tranchant. Si vous passez tout votre temps à concevoir pour chaque utilisateur imaginable, imaginable, X, Y et Z qu'un utilisateur puisse désirer, vous ne pourrez jamais rien terminer. Si vous complétez 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/design.
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 aussi été décrit comme un nutbar TDD, donc vous pourriez perdre quelques amis;)
C'est perçu comme plus lent. Ce n'est pas vrai à long terme, mais vous finirez par écrire plus de code, vous perdrez du temps à "tester sans coder". C'est un argument imparfait, mais vous avez demandé!
Il peut être difficile et long d’écrire des tests pour des données "aléatoires" telles que les flux XML et les bases de données (pas si difficile). Dernièrement, j'ai travaillé avec des flux de données météorologiques. C'est assez déroutant d'écrire des tests pour ça, du moins, vu que je n'ai pas beaucoup d'expérience avec le TDD.
Cela prend un certain temps pour y arriver et un peu pour commencer à le faire dans un projet, mais ... Je regrette toujours de ne pas avoir utilisé l'approche Test Driven lorsque je trouve des bugs stupides 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 moyens d'éviter le côté sombre de la TDD:
J'ai écrit des applications pour faire leur propre auto-test aléatoire. Le problème avec la rédaction 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 entier de nombreux tests unitaires implique que vous disposiez de composants pouvant entrer dans des états non valides, tels que des structures de données complexes. Si vous restez à l’écart des structures de données complexes, vous aurez beaucoup moins à tester.
Dans la mesure où votre application le permet, évitez toute 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 nombreux tests.
Vous allez perdre de grandes classes avec de multiples responsabilités. Vous allez aussi probablement perdre de grandes méthodes avec de multiples responsabilités. Vous pouvez perdre certaines capacités de refactorisation, mais vous perdrez également une partie de la nécessité 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 séparée.
Dave Mann
Vous devez écrire des applications d'une manière différente: une application qui les rend testables. Vous seriez surpris de voir à quel point c'est difficile au début.
Certaines personnes trouvent trop difficile de penser à ce qu'elles vont écrire avant de l'écrire. Des concepts tels que se moquer peuvent être difficiles pour certains aussi. TDD dans les applications héritées peut être très difficile si elles ne sont pas conçues pour les tests. TDD autour de cadres qui ne sont pas conviviaux pour TDD peut également être un combat.
TDD étant une compétence, les développeurs débutants peuvent avoir des difficultés au début (principalement parce qu’ils n’ont pas appris à travailler de cette façon).
Dans l’ensemble, les inconvénients sont résolus à mesure que les personnes acquièrent des compétences et que vous finissez par extraire le code "malodorant" et obtenir un système plus stable.
La personne qui a enseigné à mon équipe le développement agile ne croyait pas en la planification, vous n’avez écrit que pour la moindre exigence.
Sa devise était: refactor, refactor, refactor. J'ai fini par comprendre que le refactor signifiait "ne pas planifier à l'avance".
Permettez-moi d'ajouter que si vous appliquez les principes BDD à un projet TDD, vous pouvez atténuer quelques-uns des inconvénients majeurs é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 problèmes liés à l'application du TDD sur le lieu de travail. L'introduction de Dan à BDD peut être trouvée ici .
Je ne fais cette suggestion que parce que BDD s’attaque à certains de ces inconvénients et constitue un moyen de combler les lacunes. Vous voudrez peut-être en tenir compte lors de la collecte de vos commentaires.
TDD nécessite une certaine organisation pour votre code. Cela peut être inefficace ou difficile à lire. Ou même architecturalement faux; Par exemple, comme les méthodes private
ne peuvent pas être appelées en dehors d'une classe, vous devez les rendre non privées pour les rendre testables, ce qui est tout simplement faux.
Lorsque le code change, vous devez également modifier les tests. Avec le refactoring, cela peut représenter beaucoup de travail supplémentaire.
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 perdent leur sens.
Vous devez également vous assurer que les tests sont complets ou que, dès qu'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.