web-dev-qa-db-fra.com

Différence de temps entre le développement avec des tests unitaires et aucun test

Je suis un développeur solo avec un environnement de travail assez limité dans le temps où le temps de développement varie généralement de 1 à 4 semaines par projet, selon les exigences, l'urgence ou les deux. À tout moment, je gère environ 3-4 projets, certains ayant des échéances qui se chevauchent.

De manière attendue, la qualité du code souffre. Je n'ai pas non plus de test formel; cela revient généralement à parcourir le système jusqu'à ce qu'il se casse un peu. En conséquence, une quantité considérable de bogues s'échappe de la production, que je dois corriger et à son tour retarde mes autres projets.

C'est là qu'intervient le test unitaire. Lorsqu'il est bien fait, il devrait réduire au minimum les bogues, et encore moins ceux qui échappent à la production. D'un autre côté, l'écriture de tests peut prendre un temps considérable, ce qui ne sonne pas bien avec des projets à durée limitée comme le mien.

La question est de savoir quelle différence de temps l'écriture de code testé par unité sur du code non testé et comment cette différence de temps évolue-t-elle à mesure que la portée du projet s'élargit?

135
Revenant

Plus vous testez tard, plus il vous en coûte d'écrire des tests.

Plus un bogue vit longtemps, plus il est coûteux de le réparer.

La loi des rendements décroissants garantit que vous pouvez vous tester dans l'oubli en essayant de vous assurer qu'il n'y a pas de bugs.

Bouddha a enseigné la sagesse de la voie du milieu. Les tests sont bons. Il y a une chose trop bonne. La clé est de savoir quand vous êtes déséquilibré.

Chaque ligne de code que vous écrivez sans tests aura des coûts significativement plus élevés pour ajouter des tests plus tard que si vous aviez écrit les tests avant d'écrire le code.

Chaque ligne de code sans test sera beaucoup plus difficile à déboguer ou à réécrire.

Chaque test que vous rédigerez prendra du temps.

Chaque bogue prendra du temps à corriger.

Les fidèles vous diront de ne pas écrire une seule ligne de code sans avoir d'abord écrit un test ayant échoué. Le test garantit que vous obtenez le comportement que vous attendez. Il vous permet de changer le code rapidement sans vous soucier d'affecter le reste du système car le test prouve que le comportement est le même.

Vous devez peser tout cela contre le fait que les tests n'ajoutent pas de fonctionnalités. Le code de production ajoute des fonctionnalités. Et les fonctionnalités sont ce qui paie les factures.

De façon pragmatique, j'ajoute tous les tests avec lesquels je peux m'en tirer. J'ignore les commentaires en faveur de l'observation des tests. Je ne fais même pas confiance au code pour faire ce que je pense qu'il fait. Je fais confiance aux tests. Mais j'ai été connu pour jeter la grêle marie occasionnelle et avoir de la chance.

Cependant, de nombreux codeurs qui réussissent ne font pas TDD. Cela ne signifie pas qu'ils ne testent pas. Ils n'insistent pas de manière obsessionnelle pour que chaque ligne de code soit testée automatiquement. Même l'oncle Bob admet qu'il n'a pas testé son interface utilisateur. Il insiste également pour que vous retiriez toute logique de l'interface utilisateur.

En tant que métaphore du football (c'est le football américain), le TDD est un bon jeu au sol. Test manuel uniquement où vous écrivez une pile de code et espérez que cela fonctionne est un jeu de passe. Vous pouvez être bon dans les deux cas. Votre carrière ne fera pas les séries éliminatoires à moins que vous ne puissiez faire les deux. Cela ne fera pas le superbowl jusqu'à ce que vous appreniez quand les choisir. Mais si vous avez besoin d'un coup de pouce dans une direction particulière: les appels des officiels vont plus souvent contre moi lorsque je passe.

Si vous voulez essayer TDD, je vous recommande fortement de vous entraîner avant d'essayer de le faire au travail. TDD fait à mi-chemin, à moitié cœur et à moitié cul est une grande raison pour laquelle certains ne le respectent pas. C'est comme verser un verre d'eau dans un autre. Si vous ne vous engagez pas et que vous le faites rapidement et complètement, vous finissez par dribbler de l'eau sur toute la table.

148
candied_orange

Je suis d'accord avec le reste des réponses mais pour répondre directement à la question quelle est la différence de temps .

Roy Osherove dans son livre The Art of Unit Testing, Second Edition page 200 a fait une étude de cas de la mise en œuvre de projets de taille similaire avec des équipes similaires (en termes de compétences) pour deux clients différents où une équipe a fait des tests tandis que l'autre ne l'a pas fait.

Ses résultats étaient comme ça:

Team progress and output measured with and without tests

Ainsi, à la fin d'un projet, vous obtenez à la fois moins de temps et moins de bugs. Cela dépend bien sûr de la taille d'un projet.

112
Aki K

Il n'y a qu'une une étude que je connais qui a étudié cela dans un "environnement réel": ( Réaliser une amélioration de la qualité grâce à un développement piloté par les tests: résultats et expériences de quatre équipes industrielles . Il est coûteux de le faire de manière raisonnable, car cela signifie essentiellement que vous devez développer le même logiciel deux fois (ou idéalement encore plus souvent) avec des équipes similaires, puis jeter tout sauf un.

Les résultats de l'étude ont été une augmentation du temps de développement entre 15% et 35% (ce qui est loin d'être le chiffre 2x souvent cité par les critiques de TDD) et une diminution de la densité des défauts de pré-libération de 40% à 90% (! ). Notez que toutes les équipes n'avaient aucune expérience préalable avec TDD, donc on pourrait supposer que l'augmentation du temps peut être au moins partiellement attribuée à l'apprentissage, et donc diminuerait encore plus dans le temps, mais cela n'a pas été évalué par l'étude.

Notez que cette étude concerne le TDD, et votre question concerne les tests unitaires, qui sont des choses très différentes, mais c'est la plus proche que j'ai pu trouver.

30
Jörg W Mittag

Bien fait, le développement avec des tests unitaires peut être plus rapide même sans tenir compte des avantages des bugs supplémentaires.

Le fait est que je ne suis pas un assez bon codeur pour simplement faire fonctionner mon code dès sa compilation. Lorsque j'écris/modifie du code, je dois exécuter le code pour m'assurer qu'il fait ce que je pensais. Dans un projet, cela a fini par ressembler à:

  1. Modifier le code
  2. Compiler l'application
  3. Exécuter l'application
  4. Connectez-vous à l'application
  5. Ouvrir une fenêtre
  6. Sélectionnez un élément de cette fenêtre pour ouvrir une autre fenêtre
  7. Définissez certains contrôles dans cette fenêtre et cliquez sur un bouton

Et bien sûr, après tout cela, il fallait généralement quelques allers-retours pour bien faire les choses.

Maintenant, que se passe-t-il si j'utilise des tests unitaires? Ensuite, le processus ressemble plus à:

  1. Écrivez un test
  2. Exécutez des tests, assurez-vous qu'il échoue de la manière attendue
  3. Écrire du code
  4. Exécutez à nouveau les tests, vérifiez qu'il réussit

C'est plus facile et plus rapide que de tester manuellement l'application. Je dois encore exécuter manuellement l'application (donc je n'ai pas l'air idiot quand je rends un travail qui ne fonctionne pas du tout), mais pour la plupart, j'ai déjà résolu les problèmes, et je suis juste vérifier à ce point. En fait, je resserre généralement cette boucle en utilisant un programme qui réexécute automatiquement mes tests lorsque j'enregistre.

Cependant, cela dépend du travail dans une base de code compatible avec les tests. De nombreux projets, même ceux avec de nombreux tests, rendent la rédaction des tests difficile. Mais si vous y travaillez, vous pouvez avoir une base de code plus facile à tester via des tests automatisés qu'avec des tests manuels. En prime, vous pouvez conserver les tests automatisés et continuer à les exécuter pour éviter les régressions.

24
Winston Ewert

Bien qu'il y ait déjà beaucoup de réponses, elles sont quelque peu répétitives et je voudrais adopter une approche différente. Les tests unitaires sont précieux, si et seulement si, ils augmentent valeur commerciale. Tester pour le plaisir de tester (tests triviaux ou tautologiques), ou pour frapper une métrique arbitraire (comme la couverture de code), est une programmation culte.

Les tests sont coûteux, non seulement en temps de rédaction, mais aussi en maintenance. Ils doivent être synchronisés avec le code qu'ils testent ou ils ne valent rien. Sans parler du coût en temps de leur exécution à chaque changement. Ce n'est pas une rupture (ou une excuse pour ne pas faire les vraiment nécessaires), mais doit être pris en compte dans l'analyse coûts-avantages.

Donc, la question à poser pour décider si oui ou non (ou de quels types) tester une fonction/méthode, demandez-vous `` quelle valeur pour l'utilisateur final est-ce que je crée/sauvegarde avec ce test? ''. Si vous ne pouvez pas répondre à cette question, du haut de votre tête, alors ce test ne vaut probablement pas le coût de l'écriture/de la maintenance. (ou vous ne comprenez pas le domaine du problème, qui est un problème waaaay plus important qu'un manque de tests).

http://rbcs-us.com/documents/Why-Most-Unit-Testing-is-Waste.pdf

21
Jared Smith

Cela dépend de la personne, ainsi que de la complexité et de la forme du code avec lequel vous travaillez.

Pour moi, sur la plupart des projets, écrire des tests unitaires signifie que je fais le travail environ 25% plus rapidement. Oui, même en incluant le temps d'écrire les tests.

Parce que le fait est que le logiciel n'est pas fait lorsque vous écrivez le code. Cela se fait lorsque vous l'envoyez au client et qu'il en est satisfait. Les tests unitaires sont de loin le moyen le plus efficace que je connaisse pour détecter la plupart des bogues, isoler la plupart des bogues pour le débogage et pour avoir l'assurance que le code est bon. Vous devez faire ces choses de toute façon, faites-les bien.

9
Telastyn

La question est, quelle différence de temps l'écriture de code testé par unité sur du code non testé, et comment cette différence de temps évolue-t-elle à mesure que la portée du projet s'élargit?

Le problème s'aggrave à mesure que l'âge du projet augmente: car chaque fois que vous ajoutez de nouvelles fonctionnalités et/ou chaque fois que vous refactorisez l'implémentation existante, vous devez retester ce qui a été précédemment testé pour vous assurer qu'il fonctionne toujours. Ainsi, pour un projet à longue durée de vie (pluriannuel), vous devrez peut-être non seulement tester la fonctionnalité, mais la tester à nouveau 100 fois et plus. Pour cette raison, vous pourriez bénéficier de tests automatisés. Cependant, l'OMI est assez bon (ou même mieux) s'il s'agit de tests système automatisés plutôt que de tests unitaires automatisés.

Un deuxième problème est que les bogues peuvent être plus difficiles à trouver et à corriger s'ils ne sont pas détectés tôt. Par exemple, s'il y a un bogue dans le système et je sais qu'il fonctionnait parfaitement avant que vous apportiez votre dernier changement, alors je concentrerai mon attention sur votre dernier changement pour voir comment il a pu introduire le bogue. Mais si je ne sais pas que le système fonctionnait avant que vous apportiez votre dernière modification (car le système n'a pas été correctement testé avant votre dernière modification), alors le bogue pourrait être n'importe où.

Ce qui précède s'applique en particulier au code profond, et moins au code peu profond, par exemple ajouter de nouvelles pages Web là où il est peu probable que de nouvelles pages affectent les pages existantes.

En conséquence, une quantité considérable de bogues s'échappe de la production, que je dois corriger et à son tour retarde mes autres projets.

D'après mon expérience, ce serait inacceptable, et vous posez donc la mauvaise question. Au lieu de demander si les tests accéléreraient le développement, vous devriez vous demander ce qui rendrait le développement plus exempt de bogues.

Une meilleure question pourrait être:

  • Le test unitaire est-il le bon type de test, dont vous avez besoin pour éviter la "quantité considérable de bugs" que vous avez produits?
  • Y a-t-il d'autres mécanismes de contrôle/amélioration de la qualité (en dehors des tests unitaires) à recommander également ou à la place?

L'apprentissage est un processus en deux étapes: apprenez à bien le faire, puis apprenez à le faire plus rapidement.

4
ChrisW

Il y a eu une longue histoire de conseil des programmeurs faisant la promotion du TDD et d'autres méthodologies de test, je ne me souviendrai pas de leurs arguments et je ne serai pas d'accord avec eux, mais voici d'autres éléments à considérer qui devraient nuancer un peu:

  • Les tests ne sont pas aussi pratiques et efficaces en fonction du contexte. Je développe un logiciel web, dites-moi si vous avez un programme pour tester toute l'interface utilisateur ... en ce moment je programme des macros Excel, dois-je vraiment développer un module de test en VBA?
  • L'écriture et la maintenance du logiciel de test est un véritable travail qui compte à court terme (il est rentable à plus long terme). La rédaction de tests pertinents est également une expertise pour
  • Travailler en équipe et travailler seul n'a pas les mêmes exigences en matière de tests, car en équipe, vous devez valider, comprendre et communiquer le code que vous n'avez pas écrit.

Je dirais que le test est bon, mais assurez-vous de tester tôt et de vérifier où se trouve le gain.

3
Arthur Havlicek

Certains aspects à considérer, non mentionnés dans les autres réponses.

  • L'avantage supplémentaire/le coût supplémentaire dépendent de votre expérience avec la rédaction d'écritures
    • avec mon premier projet de test unitaire, les coûts supplémentaires ont triplé car j'ai dû apprendre beaucoup et j'ai fait beaucoup d'erreurs.
    • après 10 ans d'expérience avec tdd j'ai besoin de 25% de temps de codage en plus pour écrire les tests à l'avance.
  • avec plus de modules tdd, il y a encore besoin de test de gui manuel et de test d'intégration
  • tdd ne fonctionne que quand il est fait depuis le début.
    • l'application de tdd à un projet existant développé est coûteuse/difficile. Mais vous pouvez à la place implémenter des tests de régression.
  • les tests automatisés (unittests et autres types de tests) nécessitent const maintanace pour les faire fonctionner.
    • avoir créé un test par copier-coller peut rendre la maintenance du code test coûteuse.
    • avec une expérience croissante, testcode devient plus modulaire et plus facile à entretenir.
  • avec une expérience croissante, vous aurez le sentiment quand il vaut la peine de créer des tests automatisés et quand non.
    • par exemple, il n'y a pas de gros avantages pour les getters/setters/wrappers simples
    • je ne fais pas de tests automatisés via l'interface graphique
    • je prends soin que le businesslayer puisse être testé

Sommaire

Lorsque vous commencez avec tdd, il est difficile d'atteindre l'état "plus d'avantages que de coûts" tant que vous êtes dans un "environnement de travail contraint par le temps", surtout s'il y a des "gestionnaires intelligents" qui vous disent de "vous débarrasser du cher, inutile tester des trucs "

Remarque: avec "test unitaire", je veux dire "tester les modules de manière isolée".

Remarque: avec "test de régression", je veux dire

  • écrire du code qui produit du texte de sortie.
  • écrire du code de "test de régression" qui vérifie que le résultat de la génération est toujours le même.
  • le test de régression vous informe chaque fois que le résultat change (ce qui pourrait être correct ou un indicateur d'un nouveau bug)
  • l'idée de "tests de régression" est similaire à tests d'approbation
    • ... prendre un instantané des résultats et confirmer qu'ils n'ont pas changé.
3
k3b

Les programmeurs, comme les personnes chargées de la plupart des tâches, sous-estiment le temps qu'il faut réellement pour l'achever. Dans cet esprit, passer 10 minutes pour écrire un test peut être considéré comme le temps que l'on aurait pu passer à écrire des tonnes de code alors qu'en réalité, vous auriez passé ce temps à trouver le même nom de fonction et les mêmes paramètres que vous avez fait pendant le test . Il s'agit d'un scénario TDD.

Ne pas passer de tests, c'est un peu comme avoir une carte de crédit; nous avons tendance à dépenser plus ou à écrire plus de code. Plus de code a plus de bugs.

Au lieu de décider d'avoir une couverture totale du code ou pas du tout, je suggère de se concentrer sur la partie critique et compliquée de votre application et d'y faire des tests. Dans une application bancaire, cela pourrait être le calcul des intérêts. Un outil de diagnostic de moteur peut avoir des protocoles d'étalonnage complexes. Si vous avez travaillé sur un projet, vous savez probablement de quoi il s'agit et où se trouvent les bugs.

Commencez lentement. Développez une certaine aisance avant de juger. Vous pouvez toujours vous arrêter.

3
JeffO

Je peux me rapporter à votre expérience - notre base de code n'a eu pratiquement aucun test et était pour la plupart non testable. Il a fallu des siècles pour développer quelque chose et la correction des bugs de production a pris un temps précieux aux nouvelles fonctionnalités.

Pour une réécriture partielle, j'ai juré d'écrire des tests pour toutes les fonctionnalités de base. Au début, cela a pris beaucoup plus de temps et ma productivité a sensiblement souffert, mais après, ma productivité a été meilleure que jamais.

Une partie de cette amélioration était que j'avais moins de bugs de production, ce qui entraînait à son tour moins d'interruptions -> J'avais une meilleure concentration à tout moment.

De plus, la possibilité de tester ET de déboguer du code de manière isolée est vraiment payante - une suite de tests est largement supérieure à un système qui ne peut être débogué qu'avec une configuration manuelle, e. g. lancer votre application et naviguer vers l'écran et faire quelque chose ... peut-être quelques dizaines de fois

Mais notez qu'il y a une baisse de productivité au début, alors commencez à apprendre les tests sur certains projets où la pression du temps n'est pas déjà folle. Aussi, essayez de le démarrer sur un projet entièrement nouveau, le test unitaire du code hérité est très difficile, et cela aide lorsque vous savez à quoi ressemble une bonne suite de tests.

1
Christian Sauer

Un avantage souvent négligé de TDD est que les tests agissent comme une garantie pour vous assurer que vous n'introduisez pas de nouveaux bogues lorsque vous effectuez un changement.

L'approche TDD prend sans aucun doute plus de temps au départ, mais le point à retenir est que vous écrirez moins de code ce qui signifie moins de choses qui tournent mal. Toutes ces cloches et ces sifflets que vous incluez souvent ne feront pas partie de la base de code.

Il y a une scène dans le film Swordfish où, si ma mémoire est bonne, un pirate informatique doit travailler avec un pistolet sur la tête et être euh ... sinon distrait. Le fait est qu'il est beaucoup plus facile de travailler lorsque votre espace de tête est dans le code et que vous avez du temps de votre côté plutôt que des mois plus tard avec un client qui vous crie dessus et que d'autres priorités sont écrasées.

Les développeurs comprennent que la correction des bogues plus tard est plus coûteuse, mais retournez-la sur sa tête. Si vous pouviez être payé 500 $ par jour pour coder comment vous codez maintenant ou 1000 $ si vous avez écrit de manière TDD, vous mordriez la main de la personne qui vous fait la 2e offre. Plus tôt vous cesserez de voir le test comme une corvée et le considérerez comme une économie d'argent, mieux vous serez.

1
Robbie Dee

Juste pour compléter les réponses précédentes: rappelez-vous que les tests ne sont pas un objectif en soi. Le but des tests est que votre application se comporte comme prévu au cours de l'évolution, dans des contextes inattendus, etc.

Par conséquent, l'écriture de tests ne signifie pas prouver tous les comportements de tous les points de terminaison d'une entité. Il s'agit d'une erreur courante. Beaucoup de développeurs pensent qu'ils ont besoin de tester toutes les fonctions/objets/méthodes/propriétés/etc. Cela conduit à une charge de travail élevée et à un tas de code et de tests non pertinents. Cette approche est courante dans les grands projets, où la plupart des développeurs ne sont pas conscients du comportement holistique, mais ne peuvent voir que leur domaine d'interaction.

La bonne approche lorsqu'il s'agit de ressources et de tests clairsemés est assez évidente et de bon sens, mais pas communément formalisée: investir d'abord les ressources de développement de test sur des fonctionnalités de haut niveau, et descendre progressivement dans les spécificités . Cela signifie qu'à un moment donné, en tant que développeur solitaire, vous vous concentrerez non seulement sur les tests unitaires, mais également sur la fonctionnalité/l'intégration/etc. tester et en fonction de vos ressources de temps, progressivement dans les principales fonctions unitaires, comme vous le prévoyez et envisagez. Les tests de haut niveau fourniront les informations nécessaires pour traiter les tests de bas niveau/unitaires et pour planifier votre stratégie de développement de tests en fonction des ressources dont vous disposez.

Par exemple, vous souhaitez d'abord tester une chaîne de traitement comme une boîte noire. Si vous constatez qu'un membre de la chaîne échoue parce que le comportement n'a pas considéré une condition extrême, vous écrivez les tests qui garantissent la fonctionnalité non seulement sur ce membre mais aussi sur d'autres. Ensuite, vous livrez. Pour le cycle suivant, vous détectez que parfois le réseau tombe en panne. Vous écrivez donc des tests qui résolvent ce problème sur les modules qui pourraient être vulnérables. Etc.

0
RodolfoAP