web-dev-qa-db-fra.com

Quand les tests unitaires sont-ils inappropriés ou inutiles?

Il semble généralement admis (sur Stack Overflow au moins) qu'il devrait toujours y avoir des tests unitaires et qu'ils devraient être tenus à jour. Mais je soupçonne que les programmeurs qui font ces affirmations fonctionnent sur différents types de projets de ma part - ils travaillent sur des logiciels de haute qualité, durables et livrés où les bogues sont vraiment une mauvaise chose.

Si c'est la fin du spectre où les tests unitaires ont le plus de valeur, alors quel est l'autre bout? Où les tests unitaires sont-ils les moins utiles? Dans quelles situations ne vous dérangeriez-vous pas? Où l'effort de maintenir les tests ne vaut-il pas le coût?

109
Steve Bennett

Personnellement, je n'écrirai pas de tests unitaires dans les situations où:

  1. Le code n'a pas de succursales est trivial. Un getter qui retourne 0 n'a pas besoin d'être testé, et les modifications seront couvertes par des tests pour ses consommateurs.

  2. Le code passe simplement dans une API stable. Je suppose que la bibliothèque standard fonctionne correctement.

  3. Le code doit interagir avec d'autres systèmes déployés; alors un test d'intégration s'impose.

  4. Si le test de réussite/d'échec est quelque chose de si difficile à quantifier qu'il n'est pas mesurable de manière fiable, comme la stéganographie étant imperceptible pour l'homme.

  5. Si le test lui-même est d'un ordre de grandeur plus difficile à écrire que le code.

  6. Si le code est jetable ou code d'espace réservé. En cas de doute, testez.

107
Telastyn

À moins que vous n'écriviez du code sans le tester, vous allez toujours encourir le coût des tests.

La différence entre avoir des tests unitaires et ne pas les avoir est la différence entre le coût d'écriture du test et le coût de son exécution par rapport au coût du test à la main.

Si le coût de l'écriture d'un test unitaire est de 2 minutes et le coût de l'exécution du test unitaire est pratiquement 0, mais le coût du test manuel du code est de 1 minute, alors vous atteignez le seuil de rentabilité lorsque vous avez exécuté le test deux fois.


Pendant de nombreuses années, je pensais à tort que je n'avais pas assez de temps pour écrire des tests unitaires pour mon code. Quand j'ai fait des tests, ils étaient gonflés, des choses lourdes qui m'ont seulement encouragé à penser que je ne devrais jamais écrire des tests unitaires que lorsque je savait ils étaient nécessaires.

Récemment, j'ai été encouragé à utiliser Test Driven Development et j'ai trouvé que c'était une révélation complète. Je suis maintenant fermement convaincu que je n'ai pas le temps pas pour écrire l'unité- tests.

D'après mon expérience, en développant en gardant à l'esprit les tests, vous vous retrouvez avec des interfaces plus propres, des classes et des modules plus ciblés et généralement plus SOLID , du code testable.

Chaque fois que je travaille avec du code hérité qui n'a pas de tests unitaires et que je dois tester quelque chose manuellement, je continue de penser "ce serait tellement plus rapide si ce code avait déjà des tests unitaires". Chaque fois que je dois essayer d'ajouter une fonctionnalité de test unitaire au code avec un couplage élevé, je continue de penser "ce serait tellement plus facile s'il avait été écrit de manière découplée".

Comparaison et contraste des deux stations expérimentales que je soutiens. L'un existe depuis un certain temps et a beaucoup de code hérité, tandis que l'autre est relativement nouveau.

Lors de l'ajout de fonctionnalités à l'ancien laboratoire, il s'agit souvent de se rendre au laboratoire et de passer de nombreuses heures à travailler sur les implications des fonctionnalités dont ils ont besoin et comment je peux ajouter cette fonctionnalité sans affecter aucune des autres fonctionnalités. Le code n'est tout simplement pas configuré pour permettre des tests hors ligne, donc presque tout doit être développé en ligne. Si j'essayais de développer hors ligne, je me retrouverais avec plus objets fictifs qu'il ne serait raisonnable.

Dans le laboratoire plus récent, je peux généralement ajouter des fonctionnalités en les développant hors ligne sur mon bureau, en se moquant uniquement des choses qui sont immédiatement nécessaires, puis en ne passant que peu de temps dans le laboratoire, en résolvant les problèmes restants non résolus -ligne.


TL; DR version:

Écrivez un test lorsque le coût de l'écriture du test, plus le coût de son exécution autant de fois que nécessaire, sera probablement inférieur au coût de le tester manuellement autant de fois que nécessaire.

N'oubliez pas que si vous utilisez TDD, le coût de l'écriture des tests diminuera probablement à mesure que vous vous améliorerez, et à moins que le code ne soit absolument trivial, vous finirez probablement par exécuter vos tests plus souvent que prévu.

77
Mark Booth

Il y a un compromis entre le coût d'écriture des tests unitaires et la valeur qu'ils ajoutent. Négliger cela risque la possibilité de sur-test .

blueberryfields touché à une variable qui affecte la valeur des tests unitaires: la stabilité du projet (ou le rythme de développement). Je voudrais développer cela.

La valeur d'avoir des tests unitaires suit à peu près le tracé suivant (ouais, MS Paint!):

utility vs stability

  1. Dans la première zone, le code est instable au point que les tests unitaires devront souvent être réécrits ou entièrement mis au rebut. Il s'agit peut-être d'un prototype dont vous savez qu'il ne sera pas utilisé. Ou il peut s'agir d'un projet où vous êtes nouveau dans le langage de programmation ou le framework (et avez souvent besoin de refaire de grandes quantités de code).

    Les tests unitaires sont bons pour la refactorisation au niveau du module (c'est-à-dire la modification des fonctions). Le problème est lorsque le code est instable au niveau architectural, où des modules entiers peuvent être réécrits, supprimés ou complètement repensés. Les tests unitaires doivent alors être modifiés/supprimés/ajoutés, ce qui est un fardeau.

    À ce stade, limiter les tests d'intégration au niveau du système, ou même les tests manuels, est une meilleure approche (celle que vous choisissez à nouveau en fonction du coût d'écriture du test par rapport à la valeur que vous obtenez).

  2. Les tests unitaires ajoutent le plus de valeur dans la deuxième zone, où le code devient stable au-dessus du niveau du module, mais pas encore au niveau du module. Si vous avez une base de code stable et que vous ajoutez un module qui a de grandes chances de rester, c'est lorsque les tests unitaires sont une grande victoire.

  3. Les tests unitaires commencent également à avoir moins de valeur lorsque la stabilité du code testé est très élevée. Tester un simple wrapper d'une fonction de bibliothèque standard est un exemple où le test unitaire est exagéré.

    Si un morceau de code que vous écrivez n'aura probablement jamais besoin de changer, et que vous êtes certain de l'avoir bien (peut-être que c'est super simple?), Le test a moins de valeur.

Les tests unitaires deviennent avantageux lorsque l'utilité est supérieure au coût, comme le montre le graphique ci-dessous dans la zone II.

utility and cost

Cette ligne de coût peut être à différents niveaux pour différentes personnes (ligne 1 vs ligne 2). Si vous êtes nouveau dans le framework de test utilisé dans un projet, cela augmente le niveau et réduit l'utilitaire net . Un coût plus élevé (ligne 2) rétrécit la gamme appropriée des tests unitaires.

La ligne de coût n'a pas nécessairement besoin d'être plate. Surtout lorsque vous considérez d'autres variables telles que la taille du projet et si un cadre de test existe déjà pour le projet, cela peut être le cas.

21
jakesandlund

Personnellement, je n'écris généralement pas de tests unitaires pour:

  • Modifications du code existant non testé (hérité). Bien qu'en théorie il ne soit jamais trop tard pour commencer les tests unitaires, dans la pratique, les gains de l'ajout de tests unitaires à du code qui n'a jamais été TDDed sont faibles. Le seul avantage est la prévention de la régression; vous pouvez voir quel effet un changement fait sur des domaines que vous pouvez ou non vous attendre à ce qu'il affecte. Cependant, le test unitaire prouve seulement que le code fonctionne toujours comme il le faisait lorsque vous avez écrit le test; parce que le test n'a pas été écrit sur la base des exigences d'origine, le test ne prouve pas que l'implémentation actuelle est "correcte". Apporter des modifications testées à l'unité à une telle base de code est encore plus un cauchemar dans mon expérience que simplement effectuer le changement et exercer manuellement les zones affectées du programme; vous devez d'abord écrire un test unitaire approprié pour le code en question (qui peut nécessiter une refactorisation du code pour permettre le mocking et d'autres architectures TDD-friendly), prouver qu'il passe ET que le refactor n'a rien cassé (et quand c'est le premier test unitaire jamais écrit pour une base de code million-LoC, qui peut être difficile), puis changez le test pour vous attendre à quelque chose de différent et changez le code pour faire passer le test modifié.

  • Disposition de l'interface utilisateur et comportement de niveau supérieur. Dans certains cas, ce n'est tout simplement pas faisable; de nombreux IDE avec des "concepteurs" déclarent les éléments d'interface utilisateur comme privés, et ils ne devraient pas être publics conceptuellement. Les rendre internes nécessiterait de placer les tests unitaires dans les mêmes assemblages que le code de production (mauvais) et de les protéger, il faudrait un "proxy de test" qui expose ce que nous devons savoir sur les contrôles (et généralement pas la façon dont cela se fait). Même s'il était acceptable de rendre tous les contrôles publics et d'avoir ainsi accès à leur type, leur position, leurs propriétés de formatage, etc., les tests qui vérifient que l'interface utilisateur est conçue de la manière que vous attendez sont extrêmement fragiles. Vous pouvez généralement toujours tester les gestionnaires d'événements unitaires dans codebehinds/viewmodels, puis il est trivial de prouver que cliquer sur le bouton déclenche le gestionnaire.

  • Persistance de la base de données. Il s'agit, par définition, d'un test d'intégration. Vous vous moquez toujours du référentiel lorsque vous traitez avec du code qui transmet des données au référentiel, et vous vous moquez des crochets de l'ORM lors du développement dans le référentiel lui-même. Bien qu'il existe des "tests unitaires de base de données" et que vous puissiez "simuler" une base de données en utilisant quelque chose comme SQLite, je trouve que ces tests ne correspondent tout simplement jamais au niveau unitaire.

  • Code tiers. Vous testez VOTRE code comme vous le souhaitez et vous faites confiance que les développeurs des outils tiers que vous utilisez (y compris les composants du .NET Framework lui-même comme WPF, WCF, EF, etc.) ont fait leur travail correctement, jusqu'à preuve du contraire. Un test unitaire qui prouve que vous effectuez l'appel WCF correct ne nécessite pas réellement de traverser le proxy de service, et il n'est pas nécessaire de tester qu'un appel de service WCF particulier fonctionne correctement dans un test unitaire (que cela fonctionne ou non dépend de variables spécifiques à l'environnement qui peuvent être parfaites dans un environnement de test local mais pas dans le build-bot, ou dans les environnements QA, Staging ou UAT; c'est là que plus haut- niveau d'intégration, AAT et tests manuels ont encore de la valeur).

Dans presque tous les autres cas, une méthodologie test-first a une valeur significative dans l'application des exigences et YAGNI, empêchant la régression, identifiant les conflits d'exigences, etc. valeur et vous pouvez donc faire confiance à ce que dans d'autres tests "a une valeur pour moi en tant que développeur.

21
KeithS

Où les tests unitaires sont-ils les moins utiles?

Vous devez écrire des tests unitaires uniquement pour le code logique. La définition du code logique est ci-dessous:

Le code logique est un morceau de code qui contient une sorte de logique, aussi petite soit-elle. Il s'agit d'un code logique s'il contient un ou plusieurs des éléments suivants: une instruction IF, une boucle, des instructions switch ou case, des calculs ou tout autre type de code de prise de décision.

Si c'est la fin du spectre où les tests unitaires sont les plus utiles, alors quel est l'autre bout?

  • Le code étroitement couplé ne peut pas être facilement testé. Parfois, il ne peut pas être testé du tout. Par exemple ASP.NET les formulaires Web sont un cauchemar et parfois impossibles à tester, contrairement à ASP.NET MVC qui est construit avec des tests unitaires à l'esprit.

  • Je peux choisir de ne pas écrire de tests pour des projets très petits et simples. Cela est parfaitement justifiable dans un monde où le temps et les ressources sont limités. Oui, c'est risqué, mais c'est parfaitement acceptable lorsque l'entreprise pour laquelle vous travaillez a un temps de commercialisation limité.

Quelles situations ne vous dérangeriez pas? Où l'effort de maintenir les tests ne vaut-il pas le coût?

Une fois les tests unitaires en place, leur coût de maintenance devient très insignifiant. Les tests unitaires avec une couverture de code élevée vous donnent un sentiment de confiance précieux que le système se comporte comme vous l'attendez. La même chose s'applique aux tests d'intégration, mais ce n'est pas votre question.

Ma réponse est basée sur les hypothèses suivantes:

  • Vous connaissez la différence entre les tests unitaires, d'intégration et fonctionnels.
  • Vous maîtrisez l'un des cadres de tests unitaires et il vous faut très peu d'efforts pour écrire un test unitaire.
  • Vous n'avez pas l'impression de faire un travail supplémentaire lorsque vous écrivez des tests unitaires.

Référence: Art du test unitaire

11
CodeART

Kent Beck (l'homme derrière l'idée des tests unitaires) dit qu'il est bien de ne pas écrire de tests unitaires lorsque vous travaillez sur une idée (par exemple, un prototype) que vous pourriez jeter (si vous décidez de ne pas travailler) plus loin). Au contraire, si vous écrivez votre prototype et décidez de vous lancer, le test unitaire sera utile pour toutes les raisons évoquées dans les réponses précédentes.

9
sakisk

Vous devriez vraiment avoir des tests pour tout code que vous avez l'intention d'exécuter à plusieurs reprises.

Si vous avez l'intention de l'exécuter une fois puis de le jeter, les tests peuvent ne pas être utiles, mais si le domaine est complexe, je pourrais bien écrire les tests de toute façon pour m'aider dans ma conception et mon implémentation.

Sinon, même si la seule chose que les tests font est de confirmer que vos hypothèses sont toujours correctes, elles sont valables. Et tout code qui dure plus d'une seule session devra être modifié, auquel cas les tests se paieront 10 fois.

7
Bill Michell

Je n'ai aucune idée de inutile mais je tirerai pour quand je pense que les tests unitaires sont inappropriés en ce qu'ils ajoutent du coût sans bénéfice proportionnel:

Si notre équipe organisation/développement ne fait pas de tests unitaires, à moins que vous ne puissiez convaincre vos collègues développeurs d'exécuter, de corriger (et de créer eux-mêmes) des tests unitaires, les tests que vous créez ne seront pas exécutés. et maintenu. (Cela implique que vous travaillez sur un logiciel avec quelqu'un d'autre.)

Tout le monde ne fait pas de tests unitaires, ils sont parfois mal supportés par la culture de l'équipe locale, et dans de tels environnements, vous devez d'abord les mettre en place pour la majorité des développeurs et probablement aussi pour le processus de construction ("intégration [continue]"). ) avant de commencer à écrire des tests unitaires est logique.

Compte tenu des contraintes de temps et des dates de livraison et de "votre" position relative avec l'équipe - le temps restant jusqu'à ce que la rédaction des tests unitaires ait du sens peut facilement être des mois ou des années.

6
Martin Ba

Parfois, vous n'avez pas le temps de faire des tests unitaires. C'est bon.

Des contraintes externes existent et vous pouvez ne pas ressentir le besoin de tests à un moment donné, car vous pensez peut-être avoir une compréhension ferme de l'état du système dans son ensemble et savoir comment les changements se répercuteront sur elle.

Mais je crois toujours que vous devriez vous sentir un peu coupable à ce sujet. Donc, quand les choses commencent à s'effondrer, vous saurez que c'est parce que: "oh, ouais, je ne teste pas des trucs, commençons à tester."

Avant ce moment, la sensation de culpabilité vous aidera à éviter certains choix architecturaux qui vous empêcheraient de pouvoir automatiser les tests de code plus tard.

4
ZJR

Le coût ne réside pas dans la maintenance des tests, mais plutôt dans la non-maintenance du code de travail. Si vous divisez les tests et la mise en œuvre, vous supposez que ce que vous écrivez fonctionnera sans l'avantage de la preuve. Les tests vous donnent cette preuve et la confiance nécessaire pour maintenir le code au fil du temps. Bien sûr, vous pourriez penser que vous ne revisiterez jamais cette application que vous pensiez être un jetable, mais la réalité est que chaque fois que vous passez à écrire des logiciels est un investissement que votre employeur ne veut pas traiter à la légère. Avouons-le, les développeurs de logiciels ne sont généralement pas bon marché et le temps consacré au code à jeter est de l'argent simplement brûlé à votre employeur.

Si vous rencontrez un problème, écrivez quelques lignes de code pour voir comment quelque chose fonctionne, ou jetez simplement un exemple pour Programmers.SE, alors il s'agit vraiment de créer quelque chose que vous avez l'intention de jeter une fois que vous êtes satisfait votre compréhension d'un problème. Dans de tels cas, les tests unitaires peuvent être un obstacle à la tâche et peuvent être omis en toute sécurité. Dans tous les autres cas cependant, je dirais que les tests sont toujours appropriés, même si le programme semble relativement peu important, et vous devriez toujours viser à écrire des logiciels dans un standard professionnel avec suffisamment de support de test pour réduire la difficulté de maintenir le code dans le avenir.

2
S.Robins

La quantité et la qualité des tests unitaires à effectuer dans un projet sont directement proportionnelles au rythme de développement et à la stabilité requise du produit.

À une extrémité, pour un script unique, une démo triviale ou dans un environnement de démarrage rapide au tout début, il est peu probable que les tests unitaires importent ou soient utiles pour quoi que ce soit. Ils sont presque toujours, presque certainement, une perte de temps et de ressources.

À l'autre extrémité, lors de l'écriture de logiciels pour contrôler les stimulateurs, ou piloter des fusées, ou lorsque vous travaillez à la maintenance de systèmes logiciels stables et très complexes (par exemple, un compilateur avec une adoption intensive à travers le multivers), plusieurs couches de test unitaire, et autre couverture, sont cruciales.

[Il existe de nombreux autres facteurs spécifiques à la situation qui contribuent chacun d'une manière ou d'une autre, tels que la taille de votre équipe, votre budget, le talent de vos développeurs, votre stratégie de développement logiciel préférée, etc. le plus haut niveau d'abstraction, je trouve que ces deux font la plus grande différence]

1
blueberryfields

À mon avis, les prototypes de produit/code ne nécessitent pas de tests car ils sont souvent livrés en utilisant un processus complètement différent et à des fins complètement différentes d'un produit fini.

Imaginez ce scénario courant. Vous avez une sorte de technologie pour laquelle votre entreprise est célèbre, votre équipe de vente ou un directeur général va à un client et est en mesure de lui vendre un produit différent qui tire parti de votre technologie. Ils le vendent là-bas et promettent au client qu'il verra une version bêta dans un mois. Vous n'avez rien, même une équipe de développement.

Ce qui se passe ensuite, c'est que vous concevez rapidement une solution basée sur les points convenus/vendus au client, réunissez une équipe d'ingénieurs (souvent vedettes) et commencez à écrire du code. Ce serait bien sûr idéal si vos dates ne glissaient pas (elles le font toujours) et que vous arriviez à écrire du code en trois semaines plutôt qu'en quatre. Dans ce cas, vous avez une semaine supplémentaire, et si vous avez réussi à garantir le temps d'une équipe QA pour cette semaine - vous la testerez. En réalité, vous n'aurez pas de semaine supplémentaire et/ou votre équipe AQ ​​est réservée pour deux mois supplémentaires pour prendre en charge d'autres livraisons de produits. De plus, vous n'aurez pas de spécifications de conception, donc l'équipe QA n'aura aucune idée de ce qu'ils sont censés tester. Vous finissez donc par livrer ce que vous avez écrit avec un peu de votre propre test de fumée, qui n'est pas vraiment un test en tant que tel, simplement parce que vous n'êtes pas qualifié pour tester votre propre code. Le facteur le plus important ici, c'est que le client verra très rapidement certaines des fonctionnalités promises. Si votre prototype fait le travail, vous êtes de retour à la planche à dessin, où vous concevez et planifiez la version 1.0 actuelle - correctement cette fois.

En ce qui concerne les tests unitaires, ils sont bons pour maintenir certains contrats (je promets que mon code le fera, sinon - il est cassé). En cas de prototypage, vous ne vous souciez peut-être pas tant des contrats, car souvent vos prototypes ne vivront pas pour devenir la version 1.0, soit parce que le client est parti, soit parce que vous avez fini par développer votre version 1.0 en utilisant différentes technologies/outils. Un exemple ici serait un site Web prototype rapide écrit très rapidement sur PHP en un mois par une personne, puis vous réunissez une équipe pour le réécrire dans JEE. Même la base de données ne survivra pas souvent Ce changement.

0
maksimov

Dans une petite entreprise au rythme effréné, je trouve que les développeurs sont tellement contraints de rester productifs et d'aller vite qu'ils n'ont souvent pas la peine de tester leurs propres changements.

Nous avons une politique générale qui stipule que si vous apportez une modification (qu'il s'agisse d'une correction de bogue ou d'une amélioration), le développeur doit le tester lui-même avant de soumettre le code pour un contrôle qualité supplémentaire.

En fait, nous n'écrivons pas de tests unitaires pour la plupart des instances, car nos écrans sont principalement conçus de manière à ce que si une modification est apportée à l'écran, le reste de la logique doit être parcouru pour savoir si la modification a réussi. indépendamment.

Je trouve impératif que le développeur fasse ses propres tests, qu'ils soient documentés ou non, car ce sont eux qui savent le mieux où se trouvent les points faibles potentiels de la logique.

Je dirais donc que même si vous n'écrivez pas de tests unitaires pour chaque petite partie du système, des tests de bon sens devraient être effectués malgré tout.

0
raffi

Vous devez écrire des tests unitaires lorsque la compréhension logique requise pour maintenir ou étendre ou communiquer la logique de l'application sans défauts dépasse (ou dépassera probablement à l'avenir) la capacité du membre le moins informé de votre équipe à répondre à cette compréhension.

Cela dépend essentiellement de ce que vous pouvez garder dans votre tête à la fois. Seul, vous pouvez avoir plus de confiance que vous pouvez coder (en toute connaissance de l'impact de vos modifications) jusqu'à ce qu'une certaine complexité soit atteinte. Jusqu'à ce point, les tests unitaires sont une distraction, un obstacle et vous ralentiront. Dans une équipe, le seuil va être beaucoup plus bas, donc vous avez presque tout de suite besoin de tests unitaires pour contrer la stupidité de groupe. Si vous prenez un projet sans test de quelqu'un d'autre (ou si vous avez oublié un projet il y a quelque temps), vous voudrez écrire des tests unitaires avant de changer quoi que ce soit car votre compréhension sera nulle.

Remarque: écrire moins de code et pratiquer YAGNI réduit la complexité et donc le nombre de tests unitaires requis.

0
Benedict

Les tests unitaires sont agréables et tous, mais dans les systèmes compliqués avec beaucoup de "pièces mobiles", ils ne testent vraiment pas beaucoup.

Comme nous le savons, les tests unitaires garantissent que, étant donné l'entrée X pour une unité, vous obtenez la sortie Y où X est toute entrée appropriée et Y est toute sortie appropriée. Cependant, je trouve que la plupart du code fait ce que le développeur pensait qu'il devrait .. mais pas nécessairement ce qu'il devrait réellement faire.
(...) Avertissement: je ne sais rien des armes à feu (:)

Dans ce cas, nous avons besoin de tests système réels dans un environnement de production de modèle. Comme la fonctionnalité est testée ici de toute façon, la valeur des tests unitaires est légèrement diminuée.

Cependant, les tests unitaires sont toujours utiles dans ce cas, car ils garantissent que la fonctionnalité que vous souhaitez conserver inchangée reste en fait inchangée. Ceci est très utile car il vous donne confiance pour les domaines du système nécessitant des tests ciblés.

0
user606723

Les tests unitaires de l'OMI sont inappropriés (pas inutiles!) Dans toute base de code où cela nécessiterait des tonnes d'heures de refactoring ou même une réécriture pure et simple afin d'introduire des tests unitaires, car ce serait une vente très difficile à la direction de permettre cette allocation de temps .

0
Wayne Molina