Le développement piloté par les tests (TDD) est important ces jours-ci. Je le vois souvent recommandé comme solution à un large éventail de problèmes ici, dans Programmers SE et d’autres lieux. Je me demande pourquoi ça marche.
D'un point de vue technique, cela m'interpelle pour deux raisons:
- L'approche "test d'écriture + refactorisation jusqu'au passage" est incroyablement anti-ingénierie. Si les ingénieurs civils utilisaient cette approche pour la construction de ponts ou les concepteurs de voitures pour leurs voitures, par exemple, ils seraient en train de remodeler leurs ponts ou leurs voitures à un coût très élevé, et le résultat serait un gâchis rafraîchi sans architecture bien pensée. . La directive «refactor till pass» est souvent considérée comme un mandat pour oublier la conception architecturale et faire tout ce qui est nécessaire pour se conformer au test. En d'autres termes, le test, plutôt que l'utilisateur, définit l'exigence. Dans cette situation, comment pouvons-nous garantir de bonnes «compétences» dans les résultats, c’est-à-dire un résultat final non seulement correct, mais également extensible, robuste, facile à utiliser, fiable, sûr, sécurisé, etc.? C'est ce que fait habituellement l'architecture.
- Les tests ne peuvent pas garantir le fonctionnement d'un système. cela ne peut que montrer que ce n'est pas le cas. En d'autres termes, les tests peuvent vous montrer qu'un système contient des défauts s'il échoue à un test, mais un système qui réussit tous les tests n'est pas plus sûr qu'un système qui les échoue. La couverture de test, la qualité du test et d'autres facteurs sont cruciaux ici. Les faux sentiments de sécurité qu'un "tout vert" produit chez de nombreuses personnes ont été signalés dans les industries civile et aérospatiale comme extrêmement dangereux, car ils peuvent être interprétés comme "le système va bien", alors que cela signifie réellement "le système est aussi bon comme stratégie de test ". Souvent, la stratégie de test n'est pas vérifiée. Ou, qui teste les tests?
En résumé, je suis plus préoccupé par le bit "piloté" dans TDD que par le bit "test". Le test est parfaitement correct; ce que je ne comprends pas, c'est le pilotage du design en le réalisant.
J'aimerais voir des réponses contenant les raisons pour lesquelles TDD en génie logiciel est une bonne pratique et pourquoi les problèmes que j'ai expliqués ci-dessus ne sont pas pertinents (ou pas assez pertinents) dans le cas d'un logiciel. Je vous remercie.
Réponses:
Je pense qu'il y a une idée fausse ici. Dans la conception de logiciels, la conception est très proche du produit. En génie civil et en architecture, la conception est dissociée du produit réel: il existe des plans qui maintiennent la conception, qui sont ensuite matérialisés dans le produit fini, et qui sont séparés par une énorme quantité de temps et d’efforts.
TDD teste la conception. Mais chaque conception de voiture et de construction est également testée. Les techniques de construction sont d'abord calculées, puis testées à plus petite échelle, puis à plus grande échelle, avant d'être installées dans un bâtiment réel. Quand ils ont inventé les poutres en H et la charge, par exemple, ils se sont assurés que cela avait été essayé et réessayé avant, ils ont construit le premier pont avec celui-ci.
Les conceptions de voitures sont également testées, en concevant des prototypes et, bien sûr, en ajustant des éléments qui ne sont pas tout à fait corrects, jusqu'à ce qu'ils soient à la hauteur des attentes. Une partie de ce processus est cependant plus lente, car comme vous l'avez dit, vous ne pouvez pas jouer avec le produit. Mais chaque refonte d'une voiture s'appuie sur les expériences acquises et chaque bâtiment s'appuie sur environ mille ans de principes fondamentaux concernant l'importance de l'espace, de la lumière, de l'isolation, de la résistance, etc. Les détails sont modifiés et améliorés, à la fois dans les bâtiments et dans la refonte pour les plus récents.
En outre, les pièces sont testées. Peut-être pas exactement dans le même style que le logiciel, mais les pièces mécaniques (roues, allumeurs, câbles) sont généralement mesurées et soumises à des contraintes pour savoir si les tailles sont correctes, aucune anomalie ne doit être constatée, etc. mesurés, ils tapent des briques pour en repérer des brisées, ils peuvent en fait être testés dans une configuration ou une autre, ou ils dessinent une représentation limitée d’un grand groupe pour le mettre réellement à l’épreuve.
Ce sont toutes les choses que vous pouvez mettre en place avec TDD.
Et en effet, les tests ne sont pas une garantie. Les programmes se bloquent, les voitures tombent en panne et les bâtiments commencent à faire des choses amusantes lorsque le vent souffle. Mais ... la "sécurité" n'est pas une question booléenne. Même quand on ne peut pas tout inclure, pouvoir couvrir - disons - 99% des éventualités est mieux que couvrir seulement 50%. Ne pas tester et ensuite découvrir que l'acier n'est pas bien réglé, qu'il est fragile et qu'il se casse au premier coup de marteau lorsque vous venez de mettre en place votre structure principale est un gaspillage d'argent. Le fait que d’autres problèmes puissent encore nuire au bâtiment ne le rend pas moins stupide de permettre à un défaut facilement évitable de nuire à votre conception.
En ce qui concerne la pratique du TDD, il s’agit d’un problème d’équilibre. Le coût pour le faire d'une manière (par exemple, ne pas tester, puis récupérer les éléments plus tard), par rapport au coût d'une autre manière. C'est toujours un équilibre. Mais ne pensez pas que d’autres processus de conception n’ont pas de tests et de TDD en place.
la source
OMI, la plupart des histoires de réussite de TDD sont fausses et uniquement à des fins de marketing. Il peut y avoir très peu de succès avec cela, mais seulement pour de petites applications. Je travaille sur une grande application Silverlight où les principes de TDD sont utilisés. L'application a eu des centaines de tests mais elle n'est toujours pas stable. Plusieurs parties de l'application ne sont pas testables en raison des interactions complexes entre l'utilisateur. Tests résultants avec beaucoup de simulacres et de code difficile à comprendre.
Au début, lorsque nous avons essayé le TDD, tout semble bien aller. J'ai été capable de rédiger de nombreux tests et de simuler les éléments difficiles à tester à l'unité. Une fois que vous avez une bonne quantité de code et qu'un changement d'interface est requis, vous êtes foutu. Un grand nombre de tests doivent être corrigés et vous devrez réécrire plus de tests que la modification réelle du code.
Peter Norvig explique son point de vue sur le TDD dans le livre Coders At Work.
la source
well, you haven't done TDD right!
Test Driven Design fonctionne pour moi pour les raisons suivantes:
C'est une forme exécutable de la spécification.
Cela signifie que vous pouvez voir à partir des cas de test:
Vous écrivez la vue de l'extérieur en premier.
Le code est souvent écrit d'une manière où vous d' abord résoudre le problème, et que vous pensez de la façon dont le code que vous venez d' écrire est d'être appelé. Cela donne souvent une interface inconfortable, car il est souvent plus facile de "simplement ajouter un drapeau", etc. En pensant que "nous devons faire ceci pour que les cas de test ressemblent à CELA" dès le départ, vous inversez la situation. Cela donnera une meilleure modularité, car le code sera écrit en fonction de l'interface appelante, et non l'inverse.
Cela se traduira généralement également par un code plus propre nécessitant moins de documentation explicative.
Vous êtes fait plus vite
Puisque vous avez la spécification sous forme exécutable, vous avez terminé lorsque la suite de tests complète est terminée. Vous pouvez ajouter d'autres tests au fur et à mesure que vous clarifiez les choses, mais vous avez comme principe de base un indicateur très clair et visible des progrès accomplis.
Cela signifie que vous pouvez savoir si un travail est nécessaire ou non (est-ce que cela vous aide de réussir un test), vous finissez par avoir besoin de faire moins.
Pour ceux qui y pensent, cela peut leur être utile, je vous encourage à utiliser TDD pour votre prochaine routine de bibliothèque. Définissez lentement une spécification exécutable et faites en sorte que le code passe les tests avec succès. Une fois terminé, la spécification exécutable est disponible pour tous ceux qui ont besoin de savoir comment appeler la bibliothèque.
Étude récente
"Les résultats des études de cas indiquent que la densité de défauts avant publication des quatre produits a diminué entre 40% et 90% par rapport à des projets similaires qui n'utilisaient pas la pratique du TDD. Subjectivement, les équipes ont connu une augmentation de 15 à 35% temps de développement initial après l’adoption du TDD. " ~ Résultats et expériences de 4 équipes industrielles
la source
Le processus de création d'un logiciel n'est pas le processus d'écriture du code. Aucun projet logiciel ne devrait commencer sans un plan «à portée étendue». Tout comme un projet de pontage entre deux rives d’une rivière nécessite un tel plan d’abord.
L’approche TDD concerne (principalement) les tests unitaires (du moins c’est ce que les gens ont tendance à penser), ce qui crée les bits de code logiciel les plus bas. Lorsque toutes les caractéristiques et tous les comportements ont déjà été définis et que nous savons réellement ce que nous voulons atteindre.
En ingénierie structurelle, cela ressemble un peu à ceci:
Pour tester si le logiciel fonctionne dans son ensemble, nous concevons d'autres types de tests, tels que des tests d'utilisabilité, des tests d'intégration et des tests d'acceptation. Celles-ci doivent également être définies avant le début du travail d’écriture du code et sont exécutées une fois que les tests unitaires sont verts.
Voir le modèle V: http://en.wikipedia.org/wiki/V-Model_%28software_development%29
Voyons comment cela fonctionnerait pour un pont:
Un gouvernement local déclare à une entreprise de construction de ponts: «Nous avons besoin d’un pont pour relier ces deux points. Le pont doit pouvoir permettre un trafic important par heure et être prêt pour le 21 décembre 2012 '- c’est une définition de test d’acceptation. L’entreprise n’obtiendra pas le montant total (ou aucune somme) si elle ne réussit pas ce test.
La direction de la société décide du calendrier du projet. Ils mettent en place des équipes de travail et fixent des objectifs pour chaque équipe. Si les équipes n'atteignent pas ces objectifs, le pont ne sera pas construit à temps. Cependant - il y a un certain niveau de flexibilité ici. Si l'une des équipes rencontre des problèmes, l'entreprise peut compenser cela en changeant les exigences, en changeant de sous-traitants, en embauchant plus de personnel, etc., de sorte que l'ensemble du projet atteigne toujours l'objectif défini au point 1.
Dans une équipe responsable de la conception de composants de ponts particuliers, cela ressemble à l'exemple que j'ai donné ci-dessus. Parfois, la solution est évidente, car nous possédons un vaste corpus de connaissances en matière de création de ponts (c'est comme utiliser une bibliothèque bien testée pour le développement de logiciels - vous supposez simplement que cela fonctionne comme annoncé). Parfois, vous devez créer plusieurs modèles et les tester pour choisir le meilleur. Néanmoins, les critères sur lesquels le composant est testé sont connus à l’avance.
la source
Dans mon esprit, TDD fonctionne parce que
Plus précisément sur les points que vous soulevez
la source
TL; DR
La programmation est toujours une activité de conception, ce n'est pas de la construction. Écrire des tests unitaires après coup ne fait que confirmer que le code fait ce qu’il fait, mais pas qu’il fait quelque chose d’utile. Les échecs de test sont la valeur réelle, car ils vous permettent de détecter les erreurs plus tôt.
Code is Design
Au chapitre 7 du PPP, "Oncle Bob" parle directement de cette question. Très tôt dans le chapitre, il fait référence à un excellent article de Jack Reeves dans lequel il propose que le code soit du design (le lien renvoie à une page rassemblant les trois de ses articles sur le sujet).
Ce qui est intéressant à propos de cet argument, c’est ce qu’il souligne, contrairement à d’autres disciplines de l’ingénierie où la construction est une activité très coûteuse, la construction de logiciels est relativement libre (cliquez sur Compile dans votre IDE et vous avez votre logiciel construit). Si vous considérez l'écriture de code comme une activité de conception plutôt que comme une activité de construction, le cycle rouge-vert-refactorisation est fondamentalement un exercice de conception. Votre conception évolue au fur et à mesure que vous écrivez des tests, le code pour les satisfaire et un refactor pour intégrer le nouveau code dans le système existant.
TDD comme spécification
Les tests unitaires que vous écrivez pour TDD sont une traduction directe de la spécification telle que vous la comprenez. En écrivant du code qui satisfait au minimum votre spécification (fait passer vos tests au vert), tout le code que vous avez écrit est destiné à un but spécifique. Que cet objectif soit atteint ou non est validé par un test répétable.
Écrire des tests à la fonctionnalité
Une erreur commune dans les tests unitaires se produit lorsque vous écrivez les tests après le code, vous finissez par tester que le code fait ce qu'il fait. En d'autres termes, vous verrez des tests comme celui-ci
Bien que je suppose que ce code puisse être utile (assurez-vous que quelqu'un n'a pas fait quelque chose d'obscène avec une propriété simple). Cela ne sert pas à valider une spécification. Et comme vous l'avez dit, l'écriture de ce type de tests ne vous mène pas loin.
Bien que le vert soit bon, la valeur réside en rouge J'ai eu mon premier vrai vrai "aha" en TDD quand j'ai eu un échec de test inattendu. J'ai eu une série de tests que j'ai eu pour un cadre que je construisais. Ajoutant une nouvelle fonctionnalité, j'ai écrit un test pour cela. Puis écrit le code pour faire passer le test. Compilez, testez… obtenez le vert sur le nouveau test. Mais aussi un rouge sur un autre test que je ne pensais pas devenir rouge.
En regardant l'échec, je soupire de soulagement parce que je doute que j'aurais attrapé ce virus pendant un bon bout de temps si je n'avais pas eu ce test en place. Et c'était un très mauvais insecte à avoir. Heureusement, j'ai passé le test et il m'a dit exactement ce que je devais faire pour résoudre le problème. Sans le test, j'aurais continué à construire mon système (le bogue infectant d'autres modules dépendant de ce code) et au moment où le bogue aurait été découvert, il aurait été très difficile de le corriger correctement.
Le véritable avantage de TDD est qu’il nous permet d’apporter des modifications avec un abandon inconsidéré. C'est comme un filet de sécurité pour la programmation. Pensez à ce qui se produirait si un trapéziste commettait une erreur et tombait. Avec le net, c'est une erreur embarrassante. Sans ça, c'est une tragédie. De la même manière, TDD vous évite de transformer des erreurs démesurées en désastres qui tuent des projets.
la source
Vous ne trouverez personne qui prône le développement piloté par les tests, ni même la conception pilotée par les tests (ils sont différents), qui dit que les tests prouvent les applications. Alors appelons cela un homme de paille et le tour est joué.
Vous ne trouverez personne qui n'aime pas ou ne soit pas impressionné par le TDD qui affirme que les tests sont une perte de temps et d’effort. Bien que les tests ne prouvent pas les applications, ils sont très utiles pour détecter les erreurs.
Ces deux choses étant dites, aucune des deux parties ne fait autre chose pour effectuer des tests sur le logiciel. Les deux font des tests. Les deux font confiance aux tests pour trouver autant de bogues que possible, et utilisent des tests pour vérifier qu'un logiciel fonctionne aussi bien qu'il est possible de le découvrir à ce moment-là. Personne avec un demi-indice ne vend de logiciel sans avoir à tester et personne avec un demi-indice ne s'attend à ce que le test vienne rendre le code qu'ils vendent complètement sans mémoire.
Ainsi, la différence entre TDD et non-TDD n'est pas que des tests sont en cours. La différence réside dans la rédaction des tests. En TDD, les tests sont écrits AVANT le logiciel. Dans les tests non-TDD, les tests sont écrits après ou de concert avec le logiciel.
Le problème que j’ai vu concernant ce dernier point est que les tests ont alors tendance à cibler le logiciel en cours d’écriture plus que le résultat ou les spécifications souhaitées. Même si l'équipe de test est séparée de l'équipe de développement, cette dernière a tendance à examiner le logiciel, à l'utiliser et à écrire des tests qui le ciblent.
Ceux qui étudient le succès d'un projet ont remarqué à maintes reprises combien de fois un client exposait ce qu'il souhaitait, les responsables du développement s'en sortaient et écrivaient quelque chose, et lorsqu'ils revenaient au client en lui disant qu'ils avaient terminé. il s'avère que ce n'est absolument pas ce que le client a demandé. "Mais il passe tous les tests ..."
Le but de TDD est de casser cet "argument circulaire" et de fournir une base pour les tests qui testent un logiciel autre que le logiciel lui-même. Les tests sont écrits pour cibler le comportement souhaité par le "client". Le logiciel est ensuite écrit pour réussir ces tests.
TDD fait partie de la solution destinée à résoudre ce problème. Ce n'est pas la seule étape que vous faites. Vous devez également vous assurer que les commentaires des clients sont plus nombreux et plus fréquents.
Dans mon expérience cependant, TDD est une chose très difficile à mettre en œuvre avec succès. Il est difficile d'obtenir des tests écrits avant qu'un produit ne soit créé, car de nombreux tests automatisés nécessitent de jouer avec quelque chose pour que le logiciel d'automatisation fonctionne correctement. Il est également difficile de convaincre les développeurs qui ne sont pas habitués aux tests unitaires de le faire. Maintes et maintes fois, j'ai dit aux membres de mon équipe d'écrire les tests EN PREMIER. Je n'ai jamais vraiment eu quelqu'un pour le faire. En fin de compte, les contraintes de temps et la politique ont détruit tous les efforts, de sorte que nous ne faisons même plus de tests unitaires. Bien entendu, cela a inévitablement conduit à un couplage fort et accidentel de la conception, de sorte que même si nous le voulions, sa mise en œuvre serait désormais trop coûteuse. C’est finalement ce que TDD propose aux développeurs.
la source
Concevoir d'abord
TDD n’est pas une excuse pour ignorer la conception. J'ai vu beaucoup de gens se lancer dans le mouvement "agile" parce qu'ils pensaient pouvoir commencer à coder immédiatement. Une véritable agilité vous amènera au codage statistique beaucoup plus rapidement que les bonnes pratiques d'ingénierie (autres domaines) qui ont inspiré le processus de cascade.
Mais testez tôt
Quand on dit que les tests pilotent la conception, cela signifie simplement que l'on peut utiliser des tests très tôt dans la phase de conception, bien avant que celle-ci ne soit terminée. Faire ces tests va fortement influencer votre conception en défiant les zones grises et en la confrontant au monde réel bien avant l’achèvement du produit. vous obligeant souvent à revenir à la conception et à l’ajuster pour en tenir compte.
Test et conception ... un seul et même
À mon avis, TDD considère simplement que le test fait partie intégrante du design plutôt que de le faire à la fin pour le valider. Au fur et à mesure que vous commencez à utiliser TDD, vous vous rendez compte de la manière dont vous détruisez / endommagez votre système lors de sa conception. Personnellement, je ne fais pas toujours mes tests en premier. Bien sûr, je fais les tests (unitaires) évidents sur une interface, mais les gains réels proviennent des tests d'intégration et de spécification que je crée lorsque je pense à une nouvelle façon créative de briser ce design. Dès que je pense à un moyen, je code un test et je vois ce qui se passe. Parfois, je peux vivre avec la conséquence, dans ce cas, je déplace le test dans un projet séparé qui ne fait pas partie de la construction principale (car il continuera d'échouer).
Alors qui conduit le spectacle?
Dans TDD, piloter ici signifie simplement que vos tests influencent tellement votre conception que l’on peut sentir qu’ils la pilotent réellement. Cependant, on en reste là, et je comprends vos préoccupations, c'est un peu effrayant ... Qui dirige la série?
VOUS conduisez, pas les tests. Les tests sont là pour que, à mesure que vous avancez, vous gagniez un bon niveau de confiance en ce que vous avez créé, vous permettant ainsi de continuer à construire en sachant que tout repose sur des bases solides.
solide tant que les tests sont solides
Exactement , d’où la conduite dans le TDD. Ce ne sont pas tant les tests qui déterminent tout le processus, mais ils auront une influence aussi profonde sur la façon dont vous faites les choses, sur la façon dont vous concevez et pensez votre système, que vous déléguerez une grande partie de votre processus de réflexion aux tests et en retour. ils auront une influence profonde sur votre conception.
oui mais si je fais ça avec mon pont th ....
arrêtez-vous là… l'ingénierie logicielle est TRÈS différente de toutes les autres pratiques d'ingénierie. En réalité, le génie logiciel a beaucoup plus de points communs avec la littérature. On peut prendre un livre fini, en extraire 4 chapitres et en écrire deux nouveaux pour les remplacer. Les coller dans le livre et vous avez toujours un bon livre. Avec de bons tests et de bons logiciels, vous pouvez extraire n’importe quelle partie de votre système et le remplacer par un autre. Le coût de ce processus n’est pas beaucoup plus élevé que ce qu’il était en train de créer. En fait, si vous avez effectué vos tests et que vous leur avez permis d’influencer suffisamment votre conception, il se peut qu’elle soit moins chère que de la créer, car vous aurez la certitude que ce remplacement ne brisera pas le contenu des tests.
Si c'est tellement bon, pourquoi ne fonctionne-t-il pas toujours?
Parce que les tests nécessitent un état d'esprit TRÈS différent de celui de la construction. Tous ne sont pas en mesure de revenir en arrière et, en fait, certaines personnes ne seront pas en mesure de construire des tests appropriés simplement parce qu'elles ne peuvent pas se donner la décision de détruire leur création. Cela donnera des projets avec trop peu de tests ou juste assez pour atteindre une métrique cible (la couverture de code nous vient à l’esprit). Ils accepteront volontiers les tests de trajectoire et les tests d'exception, mais oublieront les cas extrêmes et les conditions aux limites.
D'autres s'appuieront simplement sur des tests renonçant partiellement ou totalement à la conception. Chaque membre fait sa chose puis s'intègre à l'un l'autre. Le design est avant tout un outil de communication, nous posons des enjeux pour dire que c’est là que je serai, des croquis qui indiquent que c’est là que se trouveront les portes et les fenêtres. Sans cela, votre logiciel est condamné quel que soit le nombre de tests que vous avez effectués. L'intégration et les fusions seront toujours pénibles et ils manqueront de tests aux plus hauts niveaux d'abstractions.
Pour ces équipes, TDD pourrait ne pas être la solution.
la source
Avec TDD, vous n'avez pas tendance à écrire du code qui n'est pas facile ou rapide à tester. Cela peut sembler une petite chose, mais cela peut avoir un impact profond sur un projet car il est très difficile de refactoriser, de tester, de reproduire des bogues avec des tests et de vérifier les correctifs.
Il est également plus facile pour un nouveau développeur du projet de se mettre au diapason avec un meilleur code factorisé pris en charge par des tests.
la source
J'y ai beaucoup réfléchi, même si je ne pratique pas beaucoup le TDD moi-même. Il semble exister une corrélation positive (forte?) Entre la qualité du code et le suivi de la mise à jour numérique.
1) Ma première idée est que, ce n’est (principalement) pas dû à TDD d’ajouter «meilleure qualité» au code (en tant que tel), c’est plutôt comme si TDD aidait à éliminer les pires habitudes et habitudes, et donc indirectement à améliorer la qualité.
Je dirais même que ce n'est pas le test en soi, mais le processus de rédaction de ces tests. Il est difficile d'écrire des tests pour un code incorrect, et vice versa. Et garder cela à l'arrière de la tête lors de la programmation élimine beaucoup de mauvais codes.
2) Un autre point de vue (cela devient philosophique) est de suivre les habitudes mentales du maître. Vous n’apprenez pas à devenir un maître en suivant ses "habitudes externes" (par exemple, une longue barbe est une bonne chose), vous devez apprendre ses façons de penser internes, ce qui est difficile. Et en quelque sorte, obligeant les programmeurs (novices) à suivre TDD, à rapprocher leurs façons de penser de celles du maître.
la source
Vous semblez avoir une idée fausse à la fois sur le refactoring et le TDD.
Ainsi, vous ne pouvez pas refactoriser le code tant qu'il n'est pas passé .
Et TDD, en particulier les tests unitaires (que je considère comme l’amélioration fondamentale, puisqu'un autre test me semble plutôt plausible), ne consiste pas à redéfinir un composant avant qu’il ne fonctionne. Il s'agit de concevoir un composant et de travailler sur la mise en œuvre jusqu'à ce que le composant fonctionne comme prévu.
En outre, il est important de bien comprendre que les tests unitaires consistent à tester des unités . En raison de la tendance à toujours écrire plein de choses à partir de zéro, il est important de tester de telles unités. Un ingénieur civil connaît déjà les spécifications des unités qu’il utilise (les différents matériaux) et peut s’attendre à ce qu’elles fonctionnent. Ce sont deux choses qui souvent ne s’appliquent pas aux ingénieurs en logiciel, et il est très ingénieux de tester les unités avant de les utiliser, car cela implique d’utiliser des composants testés et de haute qualité.
Si un ingénieur civil avait l’idée d’utiliser un nouveau tissu de fibres pour fabriquer un toit recouvrant un stade, vous devriez l’attendre à ce qu’il le teste en tant qu’unité, c’est-à-dire qu’il définisse les spécifications requises (par exemple poids, perméabilité, stabilité, etc.) et puis testez-le et affinez-le jusqu'à ce qu'il les rencontre.
C'est pourquoi TDD fonctionne. Parce que si vous construisez un logiciel avec des unités testées, les chances sont bien meilleures que cela fonctionne, si vous les connectez ensemble et si ce n’est pas le cas, vous pouvez vous attendre à ce que le problème se trouve dans votre code collé, en supposant que vos tests couvrent bien.
edit:
Refactoring signifie: pas de changement de fonctionnalité. Un des points de l’écriture du test unitaire est de s’assurer que le refactoring ne rompt pas le code. TDD est donc censé assurer que la refactorisation n’a pas d’effets secondaires.
La granularité n'est pas un sujet de perspective, car comme je l'ai dit, teste à l'unité les unités de test et non les systèmes, la granularité étant définie avec précision.
TDD encourage une bonne architecture. Cela vous oblige à définir et à mettre en œuvre les spécifications de toutes vos unités, ce qui vous oblige à les concevoir avant leur mise en œuvre, ce qui est tout le contraire de ce que vous semblez penser. TDD dicte la création d'unités, qui peuvent être testées individuellement et sont donc complètement découplées.
TDD ne signifie pas que je lance un test logiciel sur code spaghetti et agite les pâtes jusqu’à ce qu’elles passent.
Contrairement au génie civil, en génie logiciel, un projet évolue généralement en permanence. En génie civil, vous devez construire un pont en position A, pouvant transporter x tonnes et suffisamment large pour n véhicules par heure.
En génie logiciel, le client peut en principe décider à tout moment (éventuellement après l'achèvement des travaux) qu'il souhaite un pont à deux ponts et qu'il souhaite le connecter à l'autoroute la plus proche et qu'il souhaite qu'il s'agisse d'un pont levant, car son entreprise récemment commencé à utiliser des navires à voile.
Les ingénieurs en logiciel sont chargés de modifier les conceptions. Non pas parce que leurs conceptions sont défectueuses, mais parce que c'est le modus operandi. Si le logiciel est bien conçu, il peut être redessiné à un niveau élevé, sans avoir à réécrire tous les composants de bas niveau.
TDD consiste à concevoir un logiciel avec des composants hautement découplés testés individuellement. Bien exécuté, il vous aidera à répondre aux changements de besoins de manière significative, plus rapidement et plus sûrement que sans.
TDD ajoute des exigences au processus de développement, mais n'interdit aucune autre méthode d'assurance qualité. Certes, TDD n'offre pas la même sécurité que la vérification formelle, mais là encore, la vérification formelle est extrêmement coûteuse et impossible à utiliser au niveau du système. Et encore, si vous le vouliez, vous pourriez combiner les deux.
TDD englobe également des tests autres que les tests unitaires, effectués au niveau du système. Je trouve cela facile à expliquer mais difficile à exécuter et difficile à mesurer. En outre, ils sont tout à fait plausibles. Bien que je voie absolument leur nécessité, je ne les considère pas vraiment comme des idées.
En fin de compte, aucun outil ne résout réellement un problème. Les outils ne font que résoudre un problème plus facilement. Vous pouvez demander: Comment un ciseau m'aidera-t-il avec une belle architecture? Eh bien, si vous envisagez de faire des murs droits, les briques droites sont utiles. Et oui, d'accord, si vous donnez cet outil à un idiot, il le fera probablement claquer du pied, mais ce n'est pas la faute du burin, bien que ce ne soit pas un défaut de TDD de donner une fausse sécurité aux novices, qui n'écrit pas de bons tests.
En fin de compte, on peut dire que le TDD fonctionne beaucoup mieux que pas de TDD.
la source
Je n'aime pas que vous disiez «le test, plutôt que l'utilisateur, définit l'exigence». Je pense que vous envisagez uniquement les tests unitaires dans TDD, alors que cela couvre également les tests d'intégration.
En plus de tester les bibliothèques qui constituent la base du logiciel, écrivez les tests qui couvrent les interactions de vos utilisateurs avec le logiciel / le site Web / peu importe. Ceux-ci viennent directement des utilisateurs, et des bibliothèques comme concombre (http://cukes.info) peuvent même laisser vos utilisateurs écrire les tests eux-mêmes, en langage naturel.
TDD encourage également la flexibilité dans le code - si vous passez toujours du temps à concevoir l'architecture de quelque chose, il sera extrêmement difficile d'apporter ces modifications ultérieurement si nécessaire. Commencez par écrire quelques tests, puis écrivez un petit code qui passe ces tests. Ajoutez plus de tests, ajoutez plus de code. Si vous devez changer radicalement le code, vos tests sont toujours valables.
Et contrairement aux ponts et aux voitures, un seul logiciel peut subir d’énormes changements au cours de sa vie. Effectuer un refactoring complexe sans que les tests ne soient écrits en premier revient à poser des problèmes.
la source
Je pense que vous abordez le premier point sous le mauvais angle.
D'un point de vue théorique, nous prouvons que quelque chose fonctionne en vérifiant les points d'échec. C'est la méthode utilisée. Il existe peut-être de nombreuses autres façons de prouver que quelque chose est fonctionnel, mais TDD s’est établi en raison de la simplicité de son approche par bits: si cela ne casse pas, cela fonctionne.
En pratique, cela se traduit de manière franche en: nous pouvons maintenant passer à la chose suivante (après avoir appliqué avec succès TDD pour satisfaire tous les prédicats). Si vous abordez TDD de ce point de vue, alors il ne s'agit pas "d'écrire des tests + un refactor avant de réussir" mais plutôt d' avoir terminé, je me concentre maintenant sur la fonctionnalité suivante, qui est désormais la chose la plus importante .
Pensez comment cela s'applique au génie civil. Nous construisons un stade pouvant accueillir un public de 150000 personnes. Après avoir prouvé que l'intégrité structurelle du stade est saine, nous avons d'abord satisfait la sécurité . Nous pouvons maintenant nous concentrer sur d'autres questions qui revêtent une importance immédiate, telles que les toilettes, les stands de nourriture, les sièges, etc.… rendant l'expérience du public plus agréable. Ceci est une simplification excessive, car TDD a beaucoup plus à offrir, mais le point crucial est que vous ne créez pas la meilleure expérience utilisateur possible si vous vous concentrez à la fois sur des fonctionnalités nouvelles et intéressantes et sur le maintien de l'intégrité en même temps. Vous l'obtenez à moitié dans les deux cas. Je veux dire, comment pouvez-vous savoir exactement commentbeaucoup de toilettes et où devriez-vous placer pour 150000 personnes? J'ai rarement vu des stades s'effondrer de ma vie, mais j'ai dû faire la queue à la mi-temps à de nombreuses occasions. Cela signifie que le problème des toilettes est sans doute plus complexe et que, si les ingénieurs consacraient moins de temps à la sécurité, ils pourraient enfin être en mesure de résoudre le problème des toilettes.
Votre deuxième point est dénué de pertinence, car nous avons déjà convenu que les absolus sont une entreprise idiote et que Hank Moody dit qu'ils n'existent pas (mais je n'arrive pas à trouver une référence pour cela).
la source
TDD en génie logiciel est une bonne pratique, au même titre que la gestion des erreurs dans les applications, ainsi que la journalisation et les diagnostics (bien que cela fasse partie de la gestion des erreurs).
TDD ne doit pas être utilisé comme un outil permettant de réduire le développement de logiciels au codage d'essai et d'erreur. Néanmoins, la plupart des programmeurs observent les journaux d’exécution, surveillent les exceptions dans le débogueur ou utilisent d’autres signes d’échec / de succès au cours de leur phase de développement consistant à coder / compiler / exécuter l’application - toute la journée.
TDD n’est qu’un moyen de formaliser et d’automatiser ces étapes pour vous rendre plus productif en tant que développeur.
1) Vous ne pouvez pas comparer l’ingénierie logicielle à la construction de ponts, la flexibilité de la construction des ponts n’est pas comparable à celle de la conception d’un logiciel. Construire le pont revient à écrire le même programme encore et encore dans une machine avec des pertes. Les ponts ne peuvent pas être dupliqués et réutilisés comme les logiciels. Chaque pont est unique et doit être fabriqué. La même chose vaut pour les voitures et autres conceptions.
La chose la plus difficile en génie logiciel est de reproduire les erreurs, quand un pont tombe en panne, il est généralement très facile de déterminer ce qui a mal tourné et, en théorie, il est facile de reproduire la panne. Lorsqu'un programme d'ordinateur échoue, il peut s'agir d'une chaîne d'événements complexe qui a entraîné le système dans un état défaillant et il peut être très difficile de déterminer où se trouve l'erreur. TDD et le test unitaire facilitent le test de la robustesse des composants logiciels, des bibliothèques et des algorithmes.
2) L'utilisation de tests unitaires faibles et de scénarios de test superficiels qui ne sollicitent pas le système pour créer un faux sentiment de confiance est simplement une mauvaise pratique. Ignorer la qualité architecturale d'un système et simplement remplir les tests est évidemment aussi mauvais. Mais tricher sur les lieux de la construction d'un gratte-ciel ou d'un pont pour économiser du matériel et ne pas suivre les plans est aussi grave et cela arrive tout le temps ...
la source
Si vous acceptez le fait que plus tôt les bogues sont trouvés, moins le coût de leur réparation est rentable, alors seul le TDD en vaut la peine.
la source
TDD n'est pas vraiment une question de test. Et ce n'est certainement pas un remplacement pour de bons tests. Ce que cela vous donne est un design bien pensé, facile à consommer par le consommateur, facile à entretenir et à refactoriser ultérieurement. Ces éléments entraînent à leur tour moins de bogues et une conception de logiciel meilleure et plus adaptable. TDD vous aide également à réfléchir à vos hypothèses et à les documenter, en trouvant souvent que certaines d’entre elles étaient incorrectes. Vous les découvrez très tôt dans le processus.
Et comme avantage supplémentaire, vous disposez d'une grande suite de tests que vous pouvez exécuter pour vous assurer qu'un refactoring ne modifie pas le comportement (entrées et sorties) de votre logiciel.
la source
Je vais vous donner une réponse courte. En règle générale, TDD est considéré de manière erronée, tout comme le test unitaire. Je n'ai jamais compris les tests unitaires jusqu'à récemment, après avoir visionné une bonne vidéo de discussion technique. Essentiellement, TDD indique simplement que vous voulez que les choses suivantes fonctionnent. Ils DOIVENT être mis en œuvre. Ensuite, vous concevez le reste du logiciel comme vous le feriez normalement.
C'est un peu comme écrire des cas d'utilisation pour une bibliothèque avant de concevoir celle-ci. Sauf que vous pouvez changer le cas d'utilisation dans une bibliothèque et que vous ne pouvez pas le faire pour TDD (j'utilise TDD pour la conception d'API). Vous êtes également encouragé à ajouter plus de test et à penser aux entrées / utilisations sauvages que le test peut avoir. Je trouve cela utile lors de l'écriture de bibliothèques ou d'API où, si vous changez quelque chose, vous devez savoir que vous avez cassé quelque chose. Dans la plupart des logiciels quotidiens, je ne me dérange pas car pourquoi ai-je besoin d'un scénario de test pour un utilisateur appuyant sur un bouton ou si je veux accepter une liste CSV ou une liste avec une entrée par ligne ... Cela n'a pas d'importance, je suis autorisé pour le changer donc je ne devrais pas / ne peux pas utiliser TDD.
la source
Le logiciel est organique, quand l'ingénierie structurelle est en béton.
Lorsque vous construisez votre pont, il restera un pont et il est peu probable qu'il devienne autre chose dans un court laps de temps. Des améliorations seront apportées au fil des mois et des années, mais pas des heures et des jours comme dans les logiciels.
Lorsque vous testez isolément, vous pouvez utiliser deux types de framework. Cadre contraint et non contraint. Les frameworks sans contrainte (en .NET) vous permettent de tout tester et de tout substituer, quels que soient les modificateurs d'accès. C'est-à-dire que vous pouvez écraser et simuler des composants privés et protégés.
La plupart des projets que j'ai vus utilisent des cadres contraints (RhinoMocks, NSubstitute, Moq). Lorsque vous testez avec ces frameworks, vous devez concevoir votre application de manière à pouvoir injecter et substituer des dépendances au moment de l'exécution. Cela implique que vous devez avoir une conception faiblement couplée. Une conception faiblement couplée (si elle est bien faite) implique une meilleure séparation des préoccupations, ce qui est une bonne chose.
En résumé, j’estime que si votre conception est testable, elle est donc faiblement couplée et les problèmes sont bien séparés.
En passant, j'ai vu des applications vraiment testables, mais mal écrites du point de vue de la conception orientée objet.
la source
Ce n'est pas.
Précision: les tests automatisés valent mieux que pas de tests. Cependant, je pense personnellement que la plupart des tests unitaires sont des déchets, car ils sont généralement tautologiques (c’est-à-dire qu’ils ressortent clairement du code à tester) et qu’il est difficile de prouver qu’ils sont cohérents, non redondants et couvrent tous les cas de frontière (où des erreurs se produisent habituellement). ).
Et le plus important: une bonne conception logicielle ne tombe pas comme par magie dans les tests, car elle est annoncée par de nombreux évangélistes agiles / TDD. Toute personne affirmant le contraire fournira des liens vers des recherches scientifiques examinées par des pairs qui le prouvent, ou tout au moins une référence à un projet open source où les avantages du TDD peuvent être potentiellement étudiés par l'historique de ses modifications de code.
la source