Les tests unitaires peuvent-ils être ajoutés avec succès dans un projet de production existant? Si oui, comment et en vaut-il la peine?

J’envisage fortement d’append des tests unitaires à un projet existant en cours de production. Il a été lancé il y a 18 mois avant que je puisse vraiment voir les avantages de TDD (face palm) , alors maintenant c’est une solution assez importante avec un certain nombre de projets et je ne sais pas par où commencer pour append des tests unitaires. Ce qui me fait penser à cela, c’est que de temps en temps, un vieux bogue semble refaire surface, ou un bogue est enregistré comme corrigé sans vraiment être corrigé. Les tests unitaires réduiraient ou empêcheraient ces problèmes.

En lisant des questions similaires sur SO, j’ai vu des recommandations telles que commencer par le suivi des bogues et écrire un scénario de test pour chaque bogue afin d’éviter la régression. Cependant, je crains que je finisse par rater la grande image et que je finisse par manquer des tests fondamentaux qui auraient été inclus si j’avais utilisé TDD dès le départ.

Existe-t-il des processus / étapes à suivre pour s’assurer que les solutions existantes sont correctement testées par l’unité et ne sont pas simplement intégrées? Comment puis-je m’assurer que les tests sont de bonne qualité et ne sont pas simplement un cas où un test est préférable à l’absence de tests ?

Donc je suppose que ce que je demande aussi

  • Vaut-il la peine pour une solution existante en production?
  • Serait-il préférable d’ignorer les tests pour ce projet et de l’append dans une éventuelle ré-écriture future?
  • Qu’est-ce qui sera plus bénéfique? passer quelques semaines à append des tests ou quelques semaines à append des fonctionnalités?

(Évidemment, la réponse au troisième point dépend entièrement du fait que vous vous adressiez à la direction ou à un développeur)


Raison de la prime

Ajouter une prime pour essayer d’attirer un plus large éventail de réponses, ce qui confirme non seulement mes soupçons, mais aussi certaines bonnes raisons.

Je veux écrire cette question plus tard avec des avantages et des inconvénients pour essayer de montrer à la direction qu’il vaut la peine de passer des heures de travail à faire évoluer le développement futur du produit vers TDD. Je veux aborder ce défi et développer mon raisonnement sans mon propre sharepoint vue biaisé.

    J’ai introduit des tests unitaires sur des bases de code qui ne l’avaient pas déjà été. Le dernier grand projet auquel j’ai participé était celui où le produit était déjà en production avec des tests unitaires à mon arrivée dans l’équipe. Quand je suis parti – 2 ans plus tard – nous avions plus de 4 500 tests avec une couverture de code d’environ 33% dans une base de code avec 230 000 LOC de production (application Win-Forms financière en temps réel). Cela peut sembler faible, mais le résultat est une amélioration significative de la qualité du code et du taux de défauts, ainsi qu’une amélioration du moral et de la rentabilité.

    Cela peut être fait lorsque vous avez à la fois une compréhension et un engagement précis des parties impliquées.

    Tout d’abord, il est important de comprendre que les tests unitaires sont une compétence en soi. Vous pouvez être un programmeur très productif selon des normes “conventionnelles” et avoir toujours du mal à écrire des tests unitaires d’une manière qui s’adapte à un projet plus vaste.

    De plus, et spécialement pour votre situation, l’ajout de tests unitaires à une base de code existante sans test est également une compétence en soi. À moins que vous ou quelqu’un de votre équipe ayez de l’expérience dans l’introduction de tests unitaires à une base de code existante, je dirais que la lecture du livre de Feather est une exigence (non facultative ou fortement recommandée).

    La transition vers le test unitaire de votre code est un investissement dans les personnes et les compétences tout autant que dans la qualité de la base de code. Comprendre cela est très important en termes de mentalité et de gestion des attentes.

    Maintenant, pour vos commentaires et questions:

    Cependant, je crains que je finisse par rater la grande image et que je finisse par manquer des tests fondamentaux qui auraient été inclus si j’avais utilisé TDD dès le départ.

    Réponse courte: Oui, vous allez manquer des tests et oui, ils ne ressembleront peut-être pas à ce qu’ils auraient dans un champ vert.

    La réponse plus profonde est la suivante: peu importe. Vous commencez sans tests. Commencez à append des tests et à refactoriser au fur et à mesure. Au fur et à mesure que les niveaux de compétences s’améliorent, commencez à hausser la barre pour tous les nouveaux codes écrits ajoutés à votre projet. Continuez à améliorer, etc …

    Maintenant, en lisant entre les lignes ici, j’ai l’impression que cela vient de la mentalité de “perfection comme excuse pour ne pas agir”. Un meilleur état d’esprit consiste à se concentrer sur la confiance en soi. Donc, comme vous ne savez peut-être pas encore comment le faire, vous saurez comment aller et remplissez les blancs. Par conséquent, il n’y a aucune raison de s’inquiéter.

    Encore une fois, c’est une compétence. Vous ne pouvez pas passer de zéro test à TDD-perfection en une approche linéaire de type “processus” ou “pas à pas”. Ce sera un processus. Vos attentes doivent être de réaliser des progrès et des améliorations progressifs et progressifs. Il n’y a pas de pilule magique.

    La bonne nouvelle est que, au fil des mois (et même des années), votre code va progressivement devenir un code “correct” bien factorisé et bien testé.

    Comme une note d’accompagnement. Vous constaterez que le principal obstacle à l’introduction de tests unitaires dans une ancienne base de code est le manque de cohésion et les dépendances excessives. Vous trouverez donc probablement que la compétence la plus importante deviendra la façon de briser les dépendances existantes et le découplage du code, plutôt que d’écrire les tests unitaires proprement dits.

    Existe-t-il des processus / étapes à suivre pour s’assurer que les solutions existantes sont correctement testées par l’unité et ne sont pas simplement intégrées?

    À moins que vous ne l’ayez déjà, configurez un serveur de génération et configurez une construction d’continuous integration qui s’exécute à chaque vérification, y compris tous les tests unitaires avec couverture de code.

    Entraînez vos gens.

    Commencez quelque part et commencez à append des tests tout en progressant du sharepoint vue du client (voir ci-dessous).

    Utilisez la couverture de code comme référence pour déterminer la quantité de votre code de production à tester.

    Le temps de construction devrait toujours être RAPIDE. Si votre temps de construction est lent, vos compétences de test des unités sont à la traîne. Trouvez les tests lents et améliorez-les (découplez le code de production et testez-le isolément). Bien écrit, vous devriez facilement avoir plusieurs milliers de tests unitaires et terminer une compilation en moins de 10 minutes (environ 1 ms / test est une bonne mais très approximative directive, quelques exceptions peuvent s’appliquer comme du code en utilisant la reflection, etc. ).

    Inspecter et adapter.

    Comment puis-je m’assurer que les tests sont de bonne qualité et ne sont pas simplement un cas où un test est préférable à l’absence de tests?

    Votre propre jugement doit être votre principale source de réalité. Il n’y a pas de mésortingque pouvant remplacer les compétences.

    Si vous n’avez pas cette expérience ou ce jugement, envisagez de faire appel à quelqu’un qui le fait.

    Deux indicateurs secondaires approximatifs sont la couverture totale du code et la vitesse de construction.

    Vaut-il la peine pour une solution existante en production?

    Oui. La grande majorité de l’argent dépensé pour un système ou une solution personnalisée est dépensé après sa mise en production. Et investir dans la qualité, les personnes et les compétences ne devrait jamais être démodé.

    Serait-il préférable d’ignorer les tests pour ce projet et de l’append dans une éventuelle ré-écriture future?

    Vous devez tenir compte non seulement de l’investissement dans les personnes et les compétences, mais surtout du coût total de possession et de la durée de vie prévue du système.

    Ma réponse personnelle serait “oui bien sûr” dans la majorité des cas parce que je sais que c’est tellement mieux, mais je reconnais qu’il pourrait y avoir des exceptions.

    Qu’est-ce qui sera plus bénéfique? passer quelques semaines à append des tests ou quelques semaines à append des fonctionnalités?

    Ni. Votre approche devrait être d’append des tests à votre base de code Tandis que vous faites des progrès en termes de fonctionnalité.

    Encore une fois, c’est un investissement dans les personnes, les compétences ET la qualité de la base de code et, en tant que tel, il faudra du temps. Les membres de l’équipe doivent apprendre à briser les dépendances, à écrire des tests unitaires, à améliorer la discipline et la qualité, à concevoir des logiciels, etc. Il est important de comprendre que Avoir ces compétences mais au niveau nécessaire pour que cette approche soit réussie, alors arrêter de passer du temps à append beaucoup de tests ne fonctionnera tout simplement pas.

    De plus, l’ajout de tests unitaires à une base de code existante pour toute taille de projet importante est une entreprise LARGE qui nécessite de l’engagement et de la persistance. Vous ne pouvez pas changer quelque chose de fondamental, attendez-vous à beaucoup d’apprentissage en cours de route et demandez à votre sponsor de ne pas attendre de retour sur investissement en stoppant le stream de valeur de l’entreprise. Ça ne volera pas, et franchement ça ne devrait pas.

    Troisièmement, vous souhaitez inculquer à votre équipe des valeurs professionnelles ciblées. La qualité ne vient jamais au désortingment du client et vous ne pouvez pas aller vite sans qualité. De plus, le client vit dans un monde en mutation et votre travail consiste à faciliter son adaptation. L’alignement du client nécessite à la fois qualité et fluidité.

    Ce que vous faites est de rembourser la dette technique. Et vous le faites tout en répondant aux besoins de vos clients en constante évolution. Au fur et à mesure que la dette est remboursée, la situation s’améliore et il est plus facile de mieux servir le client et d’offrir plus de valeur. Etc. Cet élan positif est ce que vous devriez viser, car il souligne les principes du rythme durable et maintiendra et améliorera la moralité, à la fois pour votre équipe de développement, votre client et vos parties prenantes.

    J’espère que cela pourra aider

    • Vaut-il la peine pour une solution existante en production?

    Oui!

    • Serait-il préférable d’ignorer les tests pour ce projet et de l’append dans une éventuelle ré-écriture future?

    Non!

    • Qu’est-ce qui sera plus bénéfique? passer quelques semaines à append des tests ou quelques semaines à append des fonctionnalités?

    L’ajout de tests (en particulier des tests automatisés) facilite grandement le fonctionnement du projet à l’avenir, et réduit considérablement le risque que vous envoyiez des problèmes stupides à l’utilisateur.

    Les tests à priori sont ceux qui vérifient si l’interface publique de votre code (et de chaque module) fonctionne comme vous le pensez. Si vous le pouvez, essayez également d’induire chaque mode de défaillance isolé que vos modules de code devraient avoir (notez que cela peut être non sortingvial, et vous devriez faire attention à ne pas vérifier trop les erreurs, par exemple, vous ne voulez pas vraiment faire des choses comme compter le nombre de messages de journal produits en cas d’échec, car il suffit de vérifier qu’il est consigné.

    Ensuite, mettez un test pour chaque bogue actuel dans votre firebase database de bogues qui induit exactement le bogue et qui passera lorsque le bogue sera corrigé. Ensuite, corrigez ces bugs! 🙂

    L’ajout de tests prend du temps à payer, mais vous êtes remboursé plusieurs fois à la fin, car votre code est de meilleure qualité. Cela compte énormément lorsque vous essayez d’expédier une nouvelle version ou d’effectuer une maintenance.

    Le problème avec les tests unitaires de retrofitting est que vous réaliserez que vous n’avez pas pensé à injecter une dépendance ici ou à utiliser une interface là-bas, et bientôt vous réécriverez le composant entier. Si vous avez le temps de faire cela, vous vous construirez un bon filet de sécurité, mais vous pourriez avoir introduit des bogues subtils en cours de route.

    J’ai été impliqué dans de nombreux projets qui nécessitaient des tests unitaires dès le premier jour, et il n’y a pas de moyen facile de les intégrer, à moins d’une réécriture complète, qui ne se justifie généralement pas lorsque le code fonctionne et gagne de l’argent. Récemment, j’ai eu recours à l’écriture de scripts PowerShell qui exercent le code de manière à reproduire un défaut dès qu’il est généré, puis à conserver ces scripts comme une suite de tests de régression pour d’autres modifications. De cette façon, vous pouvez au moins commencer à créer des tests pour l’application sans trop les modifier, mais ils ressemblent plus à des tests de régression de bout en bout qu’aux tests unitaires appropriés.

    Je suis d’accord avec ce que la plupart des autres ont dit. L’ajout de tests au code existant est précieux. Je ne serai jamais en désaccord avec ce point, mais je voudrais append une mise en garde.

    Bien que l’ajout de tests à du code existant soit précieux, cela a un coût. Au prix de ne pas créer de nouvelles fonctionnalités. La façon dont ces deux choses s’équilibrent dépend entièrement du projet, et il existe un certain nombre de variables.

    • Combien de temps faut-il pour mettre tout ce code sous test? Journées? Semaines? Mois? Années?
    • Pour qui écrivez-vous ce code? Payer les clients? Un enseignant? Un projet open source?
    • À quoi ressemble ton horaire? Avez-vous des échéances difficiles à respecter? Avez-vous des délais à respecter?

    Encore une fois, laissez-moi insister, les tests sont précieux et vous devriez travailler pour mettre votre ancien code en test. C’est vraiment plus une question d’approche. Si vous pouvez vous permettre de tout laisser tomber et de mettre tout votre ancien code en test, faites-le. Si ce n’est pas réaliste, voici ce que vous devriez faire au moins

    • Tout nouveau code que vous écrivez doit être complètement sous test unitaire
    • Tout ancien code que vous touchez (correction de bogue, extension, etc.) doit être soumis à un test unitaire

    En outre, ce n’est pas une proposition tout ou rien. Si vous avez une équipe de quatre personnes, par exemple, et que vous pouvez respecter vos délais en mettant une ou deux personnes en mission d’essai, vous devez le faire.

    Modifier:

    Je veux écrire cette question plus tard avec des avantages et des inconvénients pour essayer de montrer à la direction qu’il vaut la peine de passer des heures de travail à faire évoluer le développement futur du produit vers TDD.

    C’est comme demander “Quels sont les avantages et les inconvénients de l’utilisation du contrôle de la source?” ou “Quels sont les avantages et les inconvénients d’interviewer des personnes avant de les embaucher?” ou “Quels sont les avantages et les inconvénients de la respiration?”

    Parfois, il n’y a qu’un seul côté à l’argument. Vous devez avoir des tests automatisés sous une forme quelconque pour tout projet de toute complexité. Non, les tests ne s’écrivent pas eux-mêmes et, oui, il faudra un peu plus de temps pour faire avancer les choses. Mais à long terme, il faudra plus de temps et plus d’argent pour réparer les bugs après coup que d’écrire des tests en amont. Période. C’est tout ce qu’on peut en dire.

    Ça vaut vraiment le coup. Notre application comporte des règles complexes de validation croisée, et nous avons récemment dû apporter des modifications importantes aux règles métier. Nous avons abouti à des conflits qui empêchaient l’utilisateur de sauvegarder. Je me suis rendu compte que cela prendrait une éternité pour régler cette question (il faut plusieurs minutes pour arriver au point où les problèmes étaient). Je voulais introduire des tests unitaires automatisés et installer le framework, mais je n’avais rien fait de plus que quelques tests fictifs pour vérifier que tout fonctionnait bien. Avec les nouvelles règles métier en main, j’ai commencé à écrire des tests. Les tests ont rapidement identifié les conditions à l’origine des conflits et nous avons pu clarifier les règles.

    Si vous écrivez des tests qui couvrent les fonctionnalités que vous ajoutez ou modifiez, vous obtiendrez un avantage immédiat. Si vous attendez une réécriture, il se peut que vous n’ayez jamais de tests automatisés.

    Vous ne devriez pas passer beaucoup de temps à écrire des tests pour des choses qui fonctionnent déjà. La plupart du temps, vous n’avez pas de spécification pour le code existant. La principale chose que vous testez est votre capacité de rétro-ingénierie. D’un autre côté, si vous voulez modifier quelque chose, vous devez couvrir cette fonctionnalité avec des tests afin que vous sachiez que vous avez apporté les modifications correctement. Et bien sûr, pour les nouvelles fonctionnalités, écrivez les tests qui échouent, puis implémentez les fonctionnalités manquantes.

    Lorsque nous avons commencé à append des tests, nous étions confrontés à une base de code d’approximativement un million d’années, avec beaucoup trop de logique dans l’interface utilisateur et dans le code de rapport.

    Une des premières choses que nous avons faites (après avoir configuré un serveur de construction continue) était d’append des tests de régression. C’étaient des tests de bout en bout.

    • Chaque suite de tests commence par initialiser la firebase database à un état connu. Nous avons en fait des dizaines de jeux de données de régression que nous conservons dans Subversion (dans un référentiel séparé de notre code, à cause de la taille). Le FixtureSetUp de chaque test copie l’un de ces jeux de données de régression dans une firebase database temporaire, puis s’exécute à partir de là.
    • La configuration de l’appareil de test exécute alors certains processus dont nous intéressons les résultats. (Cette étape est facultative – certains tests de régression existent uniquement pour tester les rapports.)
    • Ensuite, chaque test exécute un rapport, affiche le rapport dans un fichier .csv et compare le contenu de ce fichier .csv à un instantané enregistré. Ces images instantanées .csv sont stockées dans Subversion à côté de chaque jeu de données de régression. Si la sortie du rapport ne correspond pas à l’instantané enregistré, le test échoue.

    Le but des tests de régression est de vous dire si quelque chose change. Cela signifie qu’ils échouent si vous avez cassé quelque chose, mais ils échouent également si vous avez changé quelque chose express (auquel cas la solution consiste à mettre à jour le fichier d’instantané). Vous ne savez pas que les fichiers instantanés sont même corrects – il peut y avoir des bogues dans le système (et lorsque vous corrigerez ces bogues, les tests de régression échoueront).

    Néanmoins, les tests de régression ont été une énorme victoire pour nous. À peu près tous les éléments de notre système ont un rapport. Ainsi, en passant quelques semaines à tester les rapports, nous avons pu obtenir une certaine couverture sur une grande partie de notre base de code. L’écriture des tests unitaires équivalents aurait pris des mois ou des années. (Les tests unitaires nous auraient donné une bien meilleure couverture et auraient été beaucoup moins fragiles; mais je préférerais avoir quelque chose maintenant, plutôt que d’attendre des années pour la perfection.)

    Nous sums ensuite retournés et avons commencé à append des tests unitaires lorsque nous avons corrigé des bogues, ajouté des améliorations ou besoin de comprendre du code. Les tests de régression ne suppriment en rien la nécessité de tests unitaires; ils ne sont qu’un filet de sécurité de premier niveau, ce qui vous permet d’obtenir rapidement une couverture de test. Ensuite, vous pouvez commencer le refactoring pour rompre les dépendances, afin de pouvoir append des tests unitaires; et les tests de régression vous donnent la certitude que votre refactoring ne brise rien.

    Les tests de régression ont des problèmes: ils sont lents et il y a trop de raisons pour lesquelles ils peuvent se casser. Mais au moins pour nous, ils en valaient vraiment la peine. Ils ont attrapé d’innombrables insectes au cours des cinq dernières années et les attrapent en quelques heures, plutôt que d’attendre un cycle d’AQ. Nous avons toujours ces tests de régression originaux, répartis sur sept machines de construction continue différentes (distinctes de celle qui exécute les tests unitaires rapides), et nous les ajoutons même de temps en temps, car nous avons encore tellement de code que nos 6 000 + les tests unitaires ne couvrent pas.

    Je vais append ma voix et dire oui, c’est toujours utile!

    Il faut cependant garder à l’esprit certaines distinctions: boîte noire vs boîte blanche et unité vs fonctionnelle. Puisque les définitions varient, voici ce que je veux dire par:

    • Black-box = tests écrits sans connaissance particulière de l’implémentation, qui explorent généralement les cas extrêmes pour s’assurer que tout se passe comme un utilisateur naïf le souhaite.
    • White-box = tests écrits avec la connaissance de l’implémentation, qui essaient souvent d’exercer des points de défaillance bien connus.
    • Tests unitaires = tests des unités individuelles (fonctions, modules séparables, etc.). Par exemple, assurez-vous que votre classe de tableau fonctionne comme prévu et que votre fonction de comparaison de chaînes renvoie les résultats attendus pour une large gamme d’entrées.
    • Tests fonctionnels = tests de l’ensemble du système en une fois. Ces tests vont exercer une grande partie du système en même temps. Par exemple: init, ouvrez une connexion, faites des choses réelles, fermez, terminez. J’aime faire une distinction entre ceux-ci et les tests unitaires, car ils servent un objective différent.

    Lorsque j’ai ajouté des tests à un produit d’expédition en fin de partie, j’ai constaté que je obtenais le meilleur rendement des tests en boîte blanche et des tests fonctionnels . Si vous connaissez une partie du code qui est particulièrement fragile, écrivez des tests en boîte blanche pour couvrir les problèmes afin de vous assurer qu’elle ne se répète pas deux fois. De même, les tests fonctionnels du système complet constituent un test de santé utile qui vous permet de vous assurer de ne jamais casser les 10 cas d’utilisation les plus courants.

    Les tests de boîte noire et unité de petites unités sont également utiles, mais si votre temps est limité, il est préférable de les append tôt. Au moment où vous expédiez, vous avez généralement trouvé (à la dure) la majorité des cas extrêmes et des problèmes que ces tests auraient trouvés.

    Comme les autres, je vous rappellerai également les deux choses les plus importantes à propos de TDD:

    1. La création de tests est un travail continu. Il ne s’arrête jamais. Vous devriez essayer d’append de nouveaux tests chaque fois que vous écrivez un nouveau code ou modifiez le code existant.
    2. Votre suite de tests n’est jamais infaillible! Ne laissez pas le fait que vous ayez des tests vous bercer dans un faux sentiment de sécurité. Le simple fait de passer la suite de tests ne signifie pas que cela fonctionne correctement ou que vous n’avez pas introduit de régression subtile des performances, etc.

    Que ce soit la peine d’append des tests unitaires à une application en production dépend du coût de maintenance de l’application. Si l’application comporte peu de bogues et de demandes d’amélioration, cela ne vaut peut-être pas la peine. OTOH, si l’application est buggée ou fréquemment modifiée, les tests unitaires seront extrêmement bénéfiques.

    À ce stade, rappelez-vous que je parle d’append des tests unitaires de manière sélective, sans essayer de générer une suite de tests similaire à ceux qui existeraient si vous aviez pratiqué le TDD depuis le début. Par conséquent, en réponse à la seconde partie de votre deuxième question, tenez compte de l’utilisation de TDD pour votre prochain projet, qu’il s’agisse d’un nouveau projet ou d’une réécriture (excuses, mais voici un lien vers un autre livre à lire) : Logiciel orienté object croissant guidé par des tests )

    Ma réponse à votre troisième question est la même que la première: cela dépend du contexte de votre projet.

    Une autre question est de savoir si un test rétro-installé doit être effectué correctement . L’important est de s’assurer que les tests unitaires sont vraiment des tests unitaires , ce qui signifie que les tests de réadaptation requièrent une refactorisation du code existant pour permettre le découplage de vos couches / composants (cf. dependency injection, inversion du contrôle; railleur). Si vous ne le faites pas, vos tests deviennent des tests d’intégration, utiles mais moins ciblés et plus fragiles que les tests unitaires véritables.

    Vous ne mentionnez pas le langage d’implémentation, mais si vous utilisez Java, vous pouvez essayer cette approche:

    1. Dans un arbre source séparé, créez des tests de régression ou de «fumée», en utilisant un outil pour les générer, ce qui pourrait vous permettre d’atteindre une couverture proche de 80%. Ces tests exécutent tous les chemins logiques de code et vérifient à partir de là que le code fait exactement ce qu’il fait actuellement (même si un bogue est présent). Cela vous donne un filet de sécurité contre les changements de comportement par inadvertance lors de la refactorisation nécessaire pour rendre le code facilement contrôlable à la main.

    2. Pour chaque bogue que vous corrigez ou fonctionnalité ajoutée, utilisez une approche TDD pour vous assurer que le nouveau code est conçu pour être testé et placez ces tests dans une arborescence source de test normale.

    3. Le code existant devra probablement être modifié ou modifié pour le rendre testable dans le cadre de l’ajout de nouvelles fonctionnalités. vos tests de fumée vous donneront un filet de sécurité contre les régressions ou les modifications subtiles du comportement par inadvertance.

    4. Lorsque vous apportez des modifications (corrections de bogues ou fonctionnalités) via TDD, une fois terminé, il est probable que le test de fumée associé échoue. Vérifiez les défaillances comme prévu en raison des modifications apscopes et supprimez le test de fumée le moins lisible, car votre test unitaire écrit à la main couvre intégralement ce composant amélioré. Assurez-vous que votre couverture de test ne diminue pas seulement restr ou augmenter.

    5. Lors de la correction des bogues, écrivez un test d’unité défaillant qui expose d’abord le bogue.

    Je voudrais commencer cette réponse en disant que les tests unitaires sont vraiment importants car ils vous aideront à arrêter les insectes avant qu’ils ne pénètrent dans la production.

    Identifier les domaines projets / modules où les bogues ont été réintroduits. Commencez avec ces projets pour écrire des tests. Il est parfaitement logique d’écrire des tests pour de nouvelles fonctionnalités et pour corriger les bogues.

    Vaut-il la peine pour une solution existante en production?

    Oui. Vous verrez l’effet des bugs et la maintenance devenir plus facile

    Serait-il préférable d’ignorer les tests pour ce projet et de l’append dans une éventuelle ré-écriture future?

    Je recommande de commencer si maintenant.

    Qu’est-ce qui sera plus bénéfique? passer quelques semaines à append des tests ou quelques semaines à append des fonctionnalités?

    Vous posez la mauvaise question. Certainement, la fonctionnalité est plus importante que toute autre chose. Mais, vous devriez plutôt demander si passer quelques semaines à faire des tests rendra mon système plus stable. Est-ce que cela aidera mon utilisateur final? Cela aidera-t-il un nouveau développeur dans l’équipe à comprendre le projet et à s’assurer qu’il / elle n’introduit pas de bogue en raison d’un manque de compréhension de l’impact global d’un changement.

    Je suis très attaché à Refactor the Low-Hang Fruit comme réponse à la question de savoir où commencer la refactorisation. C’est un moyen d’améliorer la conception sans mordre plus que vous ne pouvez mâcher.

    Je pense que la même logique s’applique aux TDD – ou simplement aux tests unitaires: écrivez les tests dont vous avez besoin, comme vous en avez besoin; écrire des tests pour le nouveau code; écrivez des tests pour les bogues à mesure qu’ils apparaissent. Vous craignez de négliger les zones de la base de code plus difficiles à atteindre, et c’est certainement un risque, mais pour commencer: commencez! Vous pouvez atténuer le risque avec les outils de couverture de code, et le risque n’est pas (à mon avis) si important: si vous couvrez les bogues, en couvrant le nouveau code, en couvrant le code que vous regardez. , alors vous couvrez le code qui a le plus besoin de tests.

    • Oui, ça l’est. Lorsque vous commencez à append de nouvelles fonctionnalités, cela peut entraîner des modifications de code anciennes et, en conséquence, il est source de bogues potentiels.
    • (voir le premier) avant de commencer à append de nouvelles fonctionnalités, tout (ou presque) code (idéalement) devrait être couvert par des tests unitaires.
    • (voir le premier et le deuxième) :). une nouvelle fonctionnalité grandiose peut “détruire” l’ancien code travaillé.

    Oui, il peut: essayez de vous assurer que tout le code que vous écrivez à partir de maintenant a un test en place.

    Si le code qui est déjà en place doit être modifié et peut être testé, faites-le, mais il est préférable de ne pas être trop énergique pour obtenir des tests pour un code stable. Ce genre de chose a tendance à avoir un effet d’entraînement et peut devenir incontrôlable.

    Vaut-il la peine pour une solution existante en production?

    Oui. Mais vous n’avez pas à écrire tous les tests unitaires pour commencer. Ajoutez-les un par un.

    Serait-il préférable d’ignorer les tests pour ce projet et de l’append dans une éventuelle ré-écriture future?

    Non. La première fois que vous ajoutez du code qui casse la fonctionnalité, vous le regretterez.

    Qu’est-ce qui sera plus bénéfique? passer quelques semaines à append des tests ou quelques semaines à append des fonctionnalités?

    Pour les nouvelles fonctionnalités (code), c’est simple. Vous écrivez d’abord le test unitaire, puis la fonctionnalité. Pour l’ancien code, vous décidez du chemin. Vous n’avez pas besoin d’avoir tous les tests unitaires en place … Ajoutez ceux qui vous font le plus de mal sans avoir … Le temps (et les erreurs) vous diront sur lequel vous devez vous concentrer;)

    Mettre à jour

    6 ans après la réponse originale, j’ai une prise légèrement différente.

    Je pense qu’il est logique d’append des tests unitaires à tous les nouveaux codes que vous écrivez, puis de réorganiser les emplacements où vous apportez des modifications pour les rendre testables.

    Ecrire des tests en une seule fois pour tout votre code existant ne vous aidera pas – mais ne pas écrire des tests pour le nouveau code que vous écrivez (ou les zones que vous modifiez) n’a pas non plus de sens. L’ajout de tests lorsque vous refactorez / ajoutez des éléments est probablement le meilleur moyen d’append des tests et de rendre le code plus facilement maintenable dans un projet existant sans tests.

    Réponse antérieure

    Je vais soulever quelques sourcils ici 🙂

    Tout d’abord, quel est votre projet? S’il s’agit d’un compilateur, d’un langage, d’un framework ou de tout autre élément qui ne changera pas de manière fonctionnelle pendant longtemps, je pense qu’il est absolument fantastique d’append des tests unitaires.

    Cependant, si vous travaillez sur une application qui nécessitera probablement des modifications de fonctionnalités (en raison de la modification des exigences), il est inutile de faire un effort supplémentaire.

    Pourquoi?

    1. Les tests unitaires ne couvrent que les tests de code – le code fait-il ce pour quoi il a été conçu? – ne remplacent pas les tests manuels qui doivent être effectués de toute façon

    2. Les tests unitaires coûtent du temps! Maintenant, d’où je viens, c’est une denrée précieuse – et les entresockets choisissent généralement de meilleures fonctionnalités par rapport à une suite de tests complète.

    3. Si votre application est même utile aux utilisateurs, ils vont demander des modifications – vous aurez donc des versions qui feront les choses mieux, plus rapidement et feront probablement de nouvelles choses – il peut aussi y avoir beaucoup de refactoring au fur et à mesure que votre code grandit. Maintenir une suite complète de tests unitaires dans un environnement dynamic est un casse-tête.

    4. Les tests unitaires ne vont pas affecter la qualité perçue de votre produit – la qualité que l’utilisateur voit. Bien sûr, vos méthodes peuvent fonctionner exactement comme au premier jour, l’interface entre la couche de présentation et la couche de gestion peut être parfaite, mais devinez quoi? L’utilisateur s’en fiche! Demandez à de vrais testeurs de tester votre application. Et le plus souvent, ces méthodes et interfaces doivent changer de toute façon, tôt ou tard.

    Qu’est-ce qui sera plus bénéfique? passer quelques semaines à append des tests ou quelques semaines à append des fonctionnalités? – Il ya beaucoup de choses que vous pouvez faire mieux que d’écrire des tests – Ecrivez de nouvelles fonctionnalités, améliorez les performances, améliorez la convivialité, rédigez de meilleurs manuels d’aide, résolvez les bogues en attente, etc.

    Maintenant, ne vous méprenez pas – Si vous êtes absolument certain que les choses ne vont pas changer pour les 100 prochaines années, allez-y, éliminez-vous et écrivez ces tests. Les tests automatisés sont une excellente idée pour les API également, où vous ne voulez absolument pas casser le code tiers. Partout ailleurs, c’est juste quelque chose qui me fait embarquer plus tard!

    Il est peu probable que vous ayez une couverture de test significative, vous devez donc être tactique pour savoir où append des tests:

    • Comme vous l’avez mentionné, lorsque vous trouvez un bogue, c’est le bon moment pour écrire un test (pour le reproduire), puis corriger le bogue. Si vous voyez le test reproduire le bogue, vous pouvez être sûr que c’est un bon test alid. Étant donné qu’une grande partie des bogues sont des régressions (50%?), Il vaut presque toujours la peine de faire des tests de régression.
    • Lorsque vous plongez dans une zone de code pour la modifier, vous pouvez écrire des tests autour de lui. Selon la nature du code, différents tests sont appropriés. Un bon ensemble de conseils est trouvé ici .

    OTOH, ce n’est pas la peine de restr assis à écrire des tests autour du code avec lesquels les gens sont satisfaits, surtout si personne ne va le modifier. Cela n’ajoute rien (sauf peut-être comprendre le comportement du système).

    Bonne chance!

    Vous dites que vous ne voulez pas acheter un autre livre. Il suffit donc de lire l’article de Michael Feather sur le travail efficace avec le code hérité . Alors achetez le livre 🙂

    Si j’étais à votre place, je prendrais probablement une approche extérieure, en commençant par des tests fonctionnels qui exercent l’ensemble du système. Je voudrais essayer de re-documenter les exigences du système en utilisant un langage de spécification BDD comme RSpec, puis écrire des tests pour vérifier ces exigences en automatisant l’interface utilisateur.

    Ensuite, je ferais un développement basé sur les défauts pour les bogues récemment découverts, pour écrire des tests unitaires pour reproduire les problèmes et pour travailler sur les bogues jusqu’à ce que les tests réussissent.

    Pour les nouvelles fonctionnalités, je m’en tiendrai à l’approche externe: Commencez par les fonctionnalités documentées dans RSpec et vérifiées en automatisant l’interface utilisateur (qui échouera bien sûr initialement), puis ajoutez des tests unitaires plus précis au fur et à mesure de l’implémentation.

    Je ne suis pas un expert du processus, mais à partir de mon expérience, je peux vous dire que le BDD via les tests automatisés de l’interface utilisateur n’est pas facile, mais je pense que cela en vaut la peine,

    Je ne suis pas un expert chevronné de la TDD, mais je dirais que c’est extrêmement important de tester les unités autant que possible. Comme le code est déjà en place, je commencerais par mettre en place une sorte d’automatisation des tests unitaires. J’utilise TeamCity pour exercer tous les tests de mes projets, et cela vous donne un bon résumé de la manière dont les composants ont fonctionné.

    Avec cela en place, je passerais à ces composants vraiment critiques de type logique métier qui ne peuvent pas échouer. Dans mon cas, certains problèmes de sortinggomésortinge de base doivent être résolus pour différentes entrées. Je les teste donc. La raison pour laquelle je le fais est que lorsque je brûle de l’huile de minuit, il est très facile de perdre du temps à chercher des codes qui ne doivent pas être touchés, car vous savez qu’ils sont testés pour toutes les entrées possibles. (dans mon cas, il y a un nombre fini d’entrées).

    Ok, alors maintenant vous vous sentez mieux avec ces pièces critiques. Au lieu de m’asseoir et de taper tous les tests, je les attaquais au fur et à mesure. Si vous rencontrez un bogue qui doit être corrigé, écrivez les tests unitaires et éliminez-les.

    Il y a des cas où vous constaterez que le test est difficile car vous ne pouvez pas instancier une classe particulière à partir du test, vous devez donc vous en moquer. Oh, mais peut-être que vous ne pouvez pas vous moquer facilement parce que vous n’avez pas écrit sur une interface. Je prends ces scénarios “whoops” comme une opportunité pour implémenter cette interface, parce que, bien, c’est une bonne chose.

    À partir de là, j’obtiendrais votre serveur de génération ou tout autre automatisme que vous avez configuré avec un outil de couverture de code. Ils créent des graphiques à barres désagréables avec de grandes zones rouges où la couverture est faible. Maintenant, la couverture à 100% n’est pas votre objective, et une couverture à 100% ne signifie pas nécessairement que votre code est à toute épreuve, mais la barre rouge me motive définitivement lorsque j’ai du temps libre. 🙂

    Il y a tellement de bonnes réponses que je ne répéterai pas leur contenu. J’ai vérifié votre profil et il semble que vous soyez développeur C # .NET. À cause de cela, j’ajoute une référence au projet Microsoft PEX et Moles qui peut vous aider à générer automatiquement des tests unitaires pour le code hérité. Je sais que l’autogénération n’est pas la meilleure solution, mais au moins c’est le moyen de commencer. Consultez cet article très intéressant du magazine MSDN sur l’utilisation de PEX pour le code hérité .

    Ça dépend…
    Il est bon d’avoir des tests unitaires, mais vous devez déterminer qui sont vos utilisateurs et ce qu’ils sont prêts à tolérer pour obtenir un produit sans bug. Inévitablement, en remaniant votre code qui ne comporte pas de tests unitaires à l’heure actuelle, vous introduirez des bogues et de nombreux utilisateurs auront du mal à comprendre que vous rendez le produit temporairement plus défectueux pour le rendre moins défectueux à long terme. En fin de compte, ce sont les utilisateurs qui auront le dernier mot …

    Je suggère de lire un article génial d’un ingénieur TopTal, qui explique commencer à append des tests: il contient beaucoup de calculs, mais l’idée de base est la suivante:

    1) Mesurez le couplage afférent (CA) de votre code (combien une classe est utilisée par d’autres classes, ce qui signifie que le briser causerait des dommages étendus)

    2) Mesurer la complexité cyclomatique de votre code (complexité supérieure = changement de rupture plus élevé)

    Vous devez identifier les classes avec CA et CC élevés, c.-à-d. Avoir une fonction f (CA, CC) et les classes avec les plus petites différences entre les deux mésortingques doivent avoir la priorité la plus élevée pour la couverture de test.

    Pourquoi? Parce que les classes de CC très élevées mais très basses sont très importantes mais peu susceptibles de se casser. Par contre, les CA faibles mais les CC élevés risquent de casser, mais causeront moins de dégâts. Donc, vous voulez équilibrer.

    Oui. N ° Ajouter des tests.

    Aller vers une approche plus TDD permettra de mieux informer vos efforts pour append de nouvelles fonctionnalités et faciliter les tests de régression. Vérifiez-le!