web-dev-qa-db-fra.com

Pourquoi les tests unitaires échouant sont-ils considérés comme mauvais?

Dans certaines organisations, apparemment, une partie du processus de publication du logiciel consiste à utiliser les tests unitaires, mais à tout moment, tous les tests unitaires doivent réussir. Par exemple, il pourrait y avoir un écran qui montre tous les tests unitaires passant en vert - ce qui est censé être bon.

Personnellement, je pense que ce n'est pas ainsi que cela devrait être pour les raisons suivantes:

  1. Il promeut l'idée que le code devrait être parfait et qu'aucun bogue ne devrait exister - ce qui dans le monde réel est sûrement impossible pour un programme de n'importe quelle taille.

  2. Il est décourageant de penser à des tests unitaires qui échoueront. Ou certainement proposer des tests unitaires qui seraient difficiles à corriger.

  3. Si, à un moment donné, tous les tests unitaires réussissent, il n'y a pas de vue d'ensemble de l'état du logiciel à un moment donné. Il n'y a pas de feuille de route/objectif.

  4. Il dissuade d'écrire les tests unitaires à l'avance - avant la mise en œuvre.

Je dirais même que même publier un logiciel avec des tests unitaires échouant n'est pas nécessairement mauvais. Au moins alors, vous savez que certains aspects du logiciel ont des limites.

Est-ce que j'ai râté quelque chose? Pourquoi les organisations s'attendent-elles à ce que tous les tests unitaires soient réussis? N'est-ce pas vivre dans un monde de rêve? Et cela ne dissuade-t-il pas réellement une réelle compréhension du code?

94
user619818

Cette question contient plusieurs idées fausses à mon humble avis, mais la principale sur laquelle je voudrais me concentrer est qu'elle ne fait pas de distinction entre les branches de développement local, les branches de tronc, de transfert ou de libération.

Dans une branche de développement locale, il est probable que certains tests unitaires échouent à presque n'importe quel moment. Dans le coffre, ce n'est acceptable que dans une certaine mesure, mais c'est déjà un indicateur fort pour réparer les choses dès que possible. Notez que l'échec des tests unitaires dans le coffre peut déranger le reste de l'équipe, car ils exigent que tout le monde vérifie si ce n'est pas son dernier changement qui a causé l'échec.

Dans une branche de transfert ou de publication, les tests qui échouent sont une "alerte rouge", montrant qu'il y a eu quelque chose de complètement faux avec un ensemble de modifications, lorsqu'il a été fusionné du tronc dans la branche de publication.

Je dirais même que même publier un logiciel avec des tests unitaires échouant n'est pas nécessairement mauvais.

Libérer un logiciel avec quelques bogues connus en dessous d'une certaine gravité n'est pas nécessairement mauvais. Cependant, ces problèmes connus ne devraient pas provoquer un test unitaire défaillant. Sinon, après chaque test unitaire, il faudra examiner les 20 tests unitaires échoués et vérifier un par un si l'échec était acceptable ou non. Cela devient lourd, sujet aux erreurs et élimine une grande partie de l'aspect d'automatisation des tests unitaires.

Si vous avez vraiment des tests pour des bogues connus et acceptables, utilisez la fonction de désactivation/ignorer de votre outil de test unitaire (afin que vous ne les exécutiez pas par défaut, uniquement à la demande). De plus, ajoutez un ticket de faible priorité à votre outil de suivi des problèmes afin que le problème ne soit pas oublié.

275
Doc Brown

... tous les tests unitaires passent en vert - ce qui est censé être bon.

Il est bon. Pas de "supposé être" à ce sujet.

Il promeut l'idée que le code devrait être parfait et qu'aucun bogue ne devrait exister - ce qui dans le monde réel est sûrement impossible pour un programme de n'importe quelle taille.

Non. Cela prouve que vous avez testé le code aussi bien que possible jusqu'à ce point. Il est tout à fait possible que vos tests ne couvrent pas tous les cas. Si c'est le cas, toutes les erreurs finiront par apparaître dans les rapports de bogues et vous écrirez des tests [échouant] pour reproduire les problèmes, puis corrigerez l'application pour que les tests réussissent.

Il est décourageant de penser à des tests unitaires qui échoueront.

Des tests échoués ou négatifs imposent des limites fermes à ce que votre candidature acceptera et n'acceptera pas. La plupart des programmes que je connais s'opposeront à une "date" du 30 février. De plus, les développeurs, les types créatifs que nous sommes, ne veulent pas casser "leurs bébés". L'accent qui en résulte sur les cas "heureux" conduit à des applications fragiles qui se cassent - souvent.

Pour comparer l'état d'esprit du développeur et du testeur:

  • Un développeur s'arrête dès que le code fait ce qu'il veut.
  • Un testeur s'arrête lorsqu'il ne peut plus interrompre le code.

Ce sont des perspectives radicalement différentes et difficiles à concilier pour de nombreux développeurs.

Ou certainement proposer des tests unitaires qui seraient difficiles à corriger.

Vous n'écrivez pas de tests pour vous aider. Vous écrivez des tests pour vous assurer que votre code fait ce qu'il est censé faire et, plus important encore, qu'il continue pour faire ce qu'il est censé faire après vous avez changé son mise en œuvre interne.

  • Le débogage "prouve" que le code fait ce que vous voulez aujourd'hui.
  • Les tests "prouvent" que le code encore fait ce que vous voulez au fil du temps.

Si, à un moment donné, tous les tests unitaires réussissent, il n'y a pas de vue d'ensemble de l'état du logiciel à un moment donné. Il n'y a pas de feuille de route/objectif.

Le seul test "d'image" que vous donne est un instantané que le code "fonctionne" au moment où il a été testé. Comment ça évolue après c'est une autre histoire.

Il dissuade d'écrire les tests unitaires à l'avance - avant la mise en œuvre.

C'est exactement ce que vous devriez faire. Écrivez un test qui échoue (car la méthode qu'il teste n'a pas encore été implémentée), puis écrivez le code de la méthode pour faire fonctionner la méthode et, par conséquent, réussir le test. C'est à peu près le nœud du développement piloté par les tests.

Je dirais même que même publier un logiciel avec des tests unitaires échouant n'est pas nécessairement mauvais. Au moins alors, vous savez que certains aspects du logiciel ont des limites.

La publication de code avec des tests cassés signifie qu'une partie de ses fonctionnalités ne fonctionne plus comme auparavant. Cela peut être un acte délibéré parce que vous avez corrigé un bogue ou amélioré une fonctionnalité (mais ensuite vous devriez d'abord avoir changé le test pour qu'il échoue, puis codé le correctif/l'amélioration, ce qui a fait fonctionner le test Dans le processus). Plus important encore: nous sommes tous humains et nous commettons des erreurs. Si vous cassez le code, alors vous devriez casser les tests et ces tests cassés devraient sonner l'alarme.

N'est-ce pas vivre dans un monde de rêve?

Si quelque chose, c'est vivre dans le monde réel, reconnaissant que les développeurs ne sont ni omniscients ni infaillibles, que nous faisons faisons des erreurs et que nous avons besoin d'un filet de sécurité pour nous rattraper si et quand nous faire gâcher!
Entrez les tests.

Et cela ne dissuade-t-il pas réellement une réelle compréhension du code?

Peut-être. Vous n'avez pas nécessairement besoin de comprendre l'implémentation de quelque chose pour écrire des tests (cela fait partie de leur intérêt). Les tests définissent le comportement et les limites de l'application et garantissent que ceux-ci restent les mêmes sauf si vous les modifiez délibérément.

231
Phill W.

Pourquoi les tests unitaires échouent-ils comme étant mauvais?

Ils ne le sont pas - le développement piloté par les tests repose sur la notion d'échec des tests. Échec des tests unitaires pour piloter le développement, échec des tests d'acceptation pour piloter une histoire ...

Ce qui vous manque, c'est context; où les tests unitaires peuvent-ils échouer?

La réponse habituelle est que les tests unitaires ne peuvent échouer que dans des bacs à sable privés.

La notion de base est la suivante: dans un environnement où les tests échoués sont partagés, il faut un effort supplémentaire pour comprendre si une modification du code de production a introduit une nouvelle erreur. La différence entre zéro et non nul est beaucoup plus facile à détecter et à gérer que la différence entre N et non N.

De plus, garder le code partagé propre signifie que les développeurs peuvent rester sur la tâche. Lorsque je fusionne votre code, je n'ai pas besoin de changer les contextes du problème que je suis payé pour résoudre pour calibrer ma compréhension du nombre de tests devrait échouer. Si le code partagé réussit tous les tests, toute défaillance qui apparaît lorsque je fusionne dans mes modifications doit faire partie de l'interaction entre mon code et la ligne de base propre existante.

De même, lors de l'intégration, un nouveau développeur peut devenir plus productif plus rapidement, car il n'a pas besoin de passer du temps à découvrir quels tests ayant échoué sont "acceptables".

Pour être plus précis: la discipline est que les tests qui s'exécutent pendant la construction doivent réussir.

Il y a, autant que je sache, rien mal à avoir des tests qui échouent désactivé.

Par exemple, dans un environnement "d'intégration continue", vous partagerez du code sur une cadence élevée. L'intégration souvent ne signifie pas nécessairement que vos modifications doivent être prêtes à être publiées. Il existe un assortiment de techniques de déploiement sombre qui empêchent le trafic d'être libéré dans des sections du code jusqu'à ce qu'elles soient prêtes.

Ces mêmes techniques peuvent également être utilisées pour désactiver les tests ayant échoué.

L'un des exercices que j'ai effectués sur une version ponctuelle concernait le développement d'un produit avec de nombreux tests ayant échoué. La réponse que nous avons trouvée était simplement de passer par la suite, de désactiver les tests ayant échoué et documenter chacun. Cela nous a permis d'atteindre rapidement un point où tous les tests activés passaient, et la direction/donateur d'objectif/propriétaire d'or pouvait tous voir quels échanges nous avions faits pour arriver à ce point, et pouvait prendre des décisions éclairées sur le nettoyage par rapport aux nouveaux travaux.

En bref: il existe d'autres techniques de suivi travail non effectué que de laisser un tas de tests ayant échoué dans la suite en cours d'exécution.

32
VoiceOfUnreason

Il y a beaucoup de bonnes réponses, mais j'aimerais ajouter un autre angle qui, je pense, n'est pas encore bien couvert: quel est exactement l'intérêt d'avoir des tests.

Les tests unitaires ne sont pas là pour vérifier que votre code est exempt de bogues.

Je pense que c'est la principale idée fausse. Si tel était leur rôle, vous vous attendriez en effet à des tests échouant partout. Mais plutôt,

Les tests unitaires vérifient que votre code fait ce que vous pensez qu'il fait.

Dans les cas extrêmes, cela peut inclure la vérification que les bogues connus sont pas corrigés. Le point est d'avoir le contrôle sur votre base de code et d'éviter les modifications accidentelles. Lorsque vous apportez une modification, il est correct et devrait en fait casser certains tests - vous modifiez le comportement du code. Le test fraîchement cassé est maintenant une fine trace de ce que vous avez changé. Vérifiez que toutes les ruptures sont conformes à ce que vous attendez de votre changement. Si oui, mettez à jour les tests et continuez. Sinon - eh bien, votre nouveau code est définitivement buggé, revenez en arrière et corrigez-le avant de le soumettre!

Maintenant, tout ce qui précède ne fonctionne que si tous les tests sont verts, ce qui donne un fort résultat positif: c'est exactement ainsi que le code fonctionne. Les tests rouges n'ont pas cette propriété. "C'est ce que ce code ne fait pas" est rarement une information utile.

Les tests d'acceptation peuvent être ce que vous recherchez.

Il existe des tests d'acceptation. Vous pouvez écrire un ensemble de tests qui doivent être remplis pour appeler le prochain jalon. Ce sont ok pour être rouge, parce que c'est pour cela qu'ils ont été conçus. Mais ils sont très différents des tests unitaires et ne peuvent ni ne doivent les remplacer.

26
Frax

Je le vois comme l'équivalent logiciel de syndrome de la fenêtre cassée .

Les tests de travail me disent que le code est d'une qualité donnée et que les propriétaires du code s'en soucient.

Quant au moment où vous devez vous soucier de la qualité, cela dépend plutôt de la branche/référentiel de code source sur lequel vous travaillez. Le code de développement peut très bien avoir échoué aux tests indiquant un travail en cours (espérons-le!).

Des tests brisés sur une branche/un référentiel pour un système actif devraient immédiatement déclencher la sonnerie d'alarme. Si les tests cassés sont autorisés à continuer à échouer ou s'ils sont marqués de manière permanente comme "ignorer" - attendez-vous à ce que leur nombre augmente progressivement avec le temps. Si ceux-ci ne sont pas régulièrement examinés, le précédent aura été établi qu'il est acceptable de laisser les tests cassés.

Les tests cassés sont considérés de manière péjorative dans de nombreux magasins au point d'avoir restriction sur la possibilité de valider du code cassé .

23
Robbie Dee

Voici l'erreur logique sous-jacente:

S'il est bon lorsque tous les tests réussissent, il doit être mauvais si l'un des tests échoue.

Avec les tests unitaires, il EST bon quand tous les tests réussissent. C'est AUSSI BON quand un test échoue. Les deux n'ont pas besoin d'être en opposition.

Un test qui échoue est un problème qui a été détecté par votre outillage avant d'atteindre un utilisateur. C'est l'occasion de corriger une erreur avant sa publication. Et c'est une bonne chose.

Ce qui n'est pas bon permet à un test unitaire défaillant de persister dans le temps, car cela vous entraînera à ignorer vos outils et rapports. Lorsqu'un test échoue, il est temps de chercher pourquoi. Parfois, c'est un bug dans le code. Parfois, c'est un bug dans le test. Parfois, c'est un problème environnemental ou les hypothèses ont changé. Parfois, dans ces cas, le problème est temporaire, mais il restera assez longtemps pour être confondant; dans ces cas, vous devez désactiver le test (avec un rappel quelque part pour réexaminer la situation ultérieurement) jusqu'à ce que le problème soit résolu.

12
Joel Coehoorn

La réponse de Phill W est excellente. Je ne peux pas le remplacer.

Cependant, je veux me concentrer sur une autre partie qui a peut-être fait partie de la confusion.

Dans certaines organisations, apparemment, une partie du processus de publication du logiciel consiste à utiliser les tests unitaires, mais à tout moment, tous les tests unitaires doivent réussir

"à tout moment" surestime votre cas. Ce qui est important, c'est que les tests unitaires réussissent après une certaine modification a été implémentée, avant vous commencez à implémenter une autre modification.
C'est ainsi que vous gardez une trace des changements qui ont provoqué un bug. Si les tests unitaires ont commencé à échouer après implémentation du changement 25 mais avant implémentation du changement 26, alors vous savez que le changement 25 a provoqué le bogue.

Pendant l'implémentation d'un changement, bien sûr les tests unitaires pourraient échouer; cela dépend beaucoup de l'ampleur du changement. Si je suis en train de redévelopper une fonctionnalité de base, qui est plus qu'un simple tweak mineur, je vais probablement casser les tests pendant un certain temps jusqu'à ce que j'aie fini d'implémenter ma nouvelle version de la logique.


Cela peut créer des conflits quant aux règles d'équipe. Je l'ai rencontré il y a quelques semaines:

  • Chaque commit/push provoque une construction. La construction ne doit jamais échouer (si elle échoue ou si un test échoue, le développeur engagé est blâmé).
  • Chaque développeur devrait pousser ses modifications (même si elles sont incomplètes) à la fin de la journée, afin que les chefs d'équipe puissent réviser le code le matin.

L'une ou l'autre règle conviendrait. Mais les deux règles ne peuvent pas fonctionner ensemble. Si l'on me confie un changement majeur qui prend plusieurs jours, je ne serais pas en mesure d'adhérer aux deux règles en même temps. À moins que je ne commente mes modifications tous les jours et que je ne les mette en commentaire qu'après que tout a été fait; ce qui est juste un travail absurde.

Dans ce scénario, le problème ici n'est pas que les tests unitaires n'ont aucun but; c'est que le l'entreprise a des attentes irréalistes. Leur jeu de règles arbitraire ne couvre pas tous les cas, et le non-respect des règles est aveuglément considéré comme un échec du développeur plutôt que comme un échec de la règle (ce qui est le cas dans mon cas).

9
Flater

Si vous ne corrigez pas tous les tests unitaires, vous pouvez rapidement entrer dans l'état où personne ne corrige les tests interrompus.

  1. Est incorrect car les tests unitaires réussis ne montrent pas que le code est parfait

  2. Il est décourageant de trouver du code qui serait également difficile à tester, ce qui est bon du point de vue de la conception

  3. La couverture du code peut y aider (bien que ce ne soit pas une panacée). Les tests unitaires ne sont également qu'un aspect des tests - vous voulez également des tests d'intégration/d'acceptation.

6
jk.

Il promeut l'idée que le code devrait être parfait et qu'aucun bogue ne devrait exister - ce qui dans le monde réel est sûrement impossible pour un programme de n'importe quelle taille.

Pas vrai. pourquoi pensez-vous que c'est impossible? voici un exemple de programme qui fonctionne:

public class MyProgram {
  public boolean alwaysTrue() {
    return true;
  }

  @Test
  public void testAlwaysTrue() {
    assert(alwaysTrue() == true);
  }
}

Il est décourageant de penser à des tests unitaires qui échoueront. Ou certainement proposer des tests unitaires qui seraient difficiles à corriger.

Dans ce cas, ce n'est peut-être pas un test unitaire, mais un test d'intégration si c'est compliqué

Si, à un moment donné, tous les tests unitaires réussissent, il n'y a pas de vue d'ensemble de l'état du logiciel à un moment donné. Il n'y a pas de feuille de route/objectif.

vrai, cela s'appelle nit test pour une raison, il vérifie une petite unité de code.

Il dissuade d'écrire les tests unitaires à l'avance - avant la mise en œuvre.

Développeurs volonté dissuader d'écrire tout teste s'ils ne comprennent pas ses avantages par leur nature (à moins qu'ils ne viennent de l'AQ)

6
user7294900

Pour ajouter quelques points aux réponses déjà bonnes ...

mais à tout moment, tous les tests unitaires doivent réussir

Cela montre un manque de compréhension d'un processus de publication. Un échec de test peut indiquer une fonctionnalité prévue sous TDD qui n'est pas encore implémentée; ou il peut indiquer un problème connu qui a un correctif prévu pour une future version; ou cela peut simplement être quelque chose où la direction a décidé que ce n'était pas assez important pour y remédier, car les clients ne le remarqueront probablement pas. L'essentiel de tout cela est que la direction a rendu un jugement sur l'échec.

Il promeut l'idée que le code devrait être parfait et qu'aucun bogue ne devrait exister - ce qui dans le monde réel est sûrement impossible pour un programme de n'importe quelle taille.

D'autres réponses ont couvert les limites des tests.

Je ne comprends pas pourquoi vous pensez que l'élimination des bogues est un inconvénient. Si vous ne voulez pas livrer du code que vous avez vérifié (au mieux de vos capacités), faire ce qu'il est censé faire, pourquoi travaillez-vous même dans un logiciel?

Si, à un moment donné, tous les tests unitaires réussissent, il n'y a pas de vue d'ensemble de l'état du logiciel à un moment donné. Il n'y a pas de feuille de route/objectif.

Pourquoi faut-il une feuille de route?

Les tests unitaires vérifient initialement que la fonctionnalité fonctionne, mais ensuite (en tant que tests de régression) vérifient que vous n'avez rien cassé par inadvertance. Pour toutes les fonctionnalités avec des tests unitaires existants, il n'y a pas de feuille de route . Chaque fonctionnalité est connue pour fonctionner (dans les limites des tests). Si ce code est terminé, il n'a pas de feuille de route car il n'y a pas besoin de plus de travail dessus.

En tant qu'ingénieurs professionnels, nous devons éviter le piège du placage à l'or. Les amateurs peuvent se permettre de perdre du temps à bricoler sur les bords avec quelque chose qui fonctionne. En tant que professionnels, nous devons livrer un produit. Cela signifie que nous faisons fonctionner quelque chose, vérifions que cela fonctionne et passons au travail suivant.

6
Graham

Il promeut l'idée que le code devrait être parfait et qu'aucun bogue ne devrait exister

Certainement pas. Il promeut l'idée que vos tests ne devraient pas échouer, ni plus ni moins. En supposant qu'avoir des tests (même beaucoup d'entre eux) dit quoi que ce soit sur "parfait" ou "pas de bugs" est une erreur. Décider de la profondeur ou de la profondeur de vos tests doit être une partie importante de la rédaction de bons tests, et la raison pour laquelle nous avons des catégories de tests distinctes (tests "unitaires", tests d'intégration, "scénarios" au sens concombre, etc.).

Il est décourageant de penser à des tests unitaires qui échoueront. Ou certainement proposer des tests unitaires qui seraient difficiles à corriger.

Dans le développement piloté par les tests, il est obligatoire que chaque les tests unitaires échouent d'abord, avant de commencer à coder. C'est ce qu'on appelle le "cycle rouge-vert" (ou "cycle refactor rouge-vert") pour cette raison même.

  • Sans échec du test, vous ne savez pas si le code est réellement testé par le test. Les deux pourraient ne pas être liés du tout.
  • En changeant le code en exactement faites passer le test du rouge au vert, rien de plus et rien de moins, vous pouvez être assez sûr que votre code fait ce qu'il est censé faire, et pas beaucoup plus ( dont vous pourriez ne jamais avoir besoin).

Si, à un moment donné, tous les tests unitaires réussissent, il n'y a pas de vue d'ensemble de l'état du logiciel à un moment donné. Il n'y a pas de feuille de route/objectif.

Les tests sont plus une sorte de micro-objectif. Dans le développement piloté par les tests, le programmeur écrira d'abord un test (singulier), puis aura un objectif clair d'implémenter du code; puis le prochain test, et ainsi de suite.

La fonction des tests n'est pas d'être complète avant l'écriture du code.

Lorsque cela est fait correctement, dans un langage et avec une bibliothèque de tests bien adaptée à cette approche, cela peut en fait accélérer considérablement le développement, car les messages d'erreur (exceptions/stacktraces) peuvent diriger directement le développeur vers l'endroit où il doit effectuer le travail. suivant.

Il dissuade d'écrire les tests unitaires à l'avance - avant la mise en œuvre.

Je ne vois pas comment cette affirmation serait vraie. L'écriture des tests devrait idéalement être un partie de l'implémentation.

Est-ce que j'ai râté quelque chose? Pourquoi les organisations s'attendent-elles à ce que tous les tests unitaires soient réussis?

Parce que les organisations s'attendent à ce que les tests soient pertinents pour le code. L'écriture de tests réussis signifie que vous avez documenté une partie de votre application et que vous avez prouvé que l'application fait ce qu'elle (le test) dit. Rien de plus et rien de moins.

De plus, une très grande partie des tests est la "régression". Vous voulez être en mesure de développer ou de refaçonner un nouveau code en toute confiance. Avoir une grande quantité de tests verts vous permet de le faire.

Cela va du niveau organisationnel au niveau psychologique. Un développeur qui sait que ses erreurs seront très probablement détectées par les tests sera beaucoup plus libre de trouver des solutions intelligentes et audacieuses pour les problèmes qu'il doit résoudre. D'un autre côté, un développeur qui n'a pas de tests sera, après un certain temps, au point mort (par peur) car il ne sait jamais si un changement qu'il fait interrompt le reste de l'application.

N'est-ce pas vivre dans un monde de rêve?

Non. Travailler avec une application pilotée par les tests est une pure joie - à moins que vous n'aimiez tout simplement pas le concept pour une raison quelconque ("plus d'efforts", etc., etc.) dont nous pourrons discuter dans une autre question.

Et cela ne dissuade-t-il pas réellement une réelle compréhension du code?

Absolument pas, pourquoi le ferait-il?

Vous trouverez de nombreux grands projets open source (pour lesquels la gestion de la "compréhension" et du savoir-faire sur le code est un sujet très urgent) qui utilisent réellement les tests comme documentation principale du logiciel, en plus d'être des tests, fournissent également des exemples réels, fonctionnels et syntaxiquement corrects aux utilisateurs ou aux développeurs de l'application/bibliothèque. Cela fonctionne souvent à merveille.

Évidemment, écrire de mauvais tests est mauvais. Mais cela n'a rien à voir avec la fonction des tests en soi.

4
AnoE

(D'après mes commentaires originaux)

Il y a une différence entre les fonctionnalités requises et les objectifs futurs. Les tests sont pour la fonctionnalité requise: ils sont précis, formels, exécutables et s'ils échouent, le logiciel ne fonctionne pas. Les objectifs futurs pourraient ne pas être précis ou formels, et encore moins exécutables, il est donc préférable de les laisser en langage naturel, comme dans les suiveurs de problèmes/bogues, la documentation, les commentaires, etc.

Comme exercice, essayez de remplacer l'expression "test unitaire" dans votre question par "erreur du compilateur" (ou "erreur de syntaxe", s'il n'y a pas de compilateur). Il est évident qu'une version ne devrait pas contenir d'erreurs de compilation, car elle serait inutilisable; pourtant, les erreurs de compilation et les erreurs de syntaxe sont la situation normale sur la machine d'un développeur lors de l'écriture de code. Les erreurs ne disparaissent que lorsqu'elles sont terminées; et c'est exactement quand le code doit être poussé. Remplacez maintenant "erreur du compilateur" dans ce paragraphe par "test unitaire" :)

3
Warbo

Les réponses existantes sont certainement bonnes, mais je n'ai vu personne répondre à cette idée fausse fondamentale dans la question:

à tout moment, tous les tests unitaires doivent réussir

Non. Assurément, ce ne sera pas vrai. Pendant que je développe un logiciel, NCrunch est le plus souvent marron (échec de la construction) ou rouge (échec du test).

Où NCrunch doit être vert (tous les tests réussissent), c'est quand je suis prêt à pousser un commit sur le serveur de contrôle de code source, car à ce stade, d'autres peuvent dépendre de mon code.

Cela alimente également le sujet de la création de nouveaux tests: les tests doivent affirmer la logique et le comportement du code. Conditions aux limites, conditions de panne, etc. Lorsque j'écris de nouveaux tests, j'essaie d'identifier ces "points chauds" dans le code.

Les tests unitaires documentent comment je m'attends à ce que mon code soit appelé - conditions préalables, sorties attendues, etc.

Si un test échoue après un changement, je dois décider si le code ou le test est erroné.


Par ailleurs, les tests unitaires vont parfois de pair avec le développement piloté par les tests. L'un des principes de TDD est que les tests brisés sont vos repères. Lorsqu'un test échoue, vous devez corriger le code afin que le test réussisse. Voici un exemple concret du début de cette semaine:

Background: J'ai écrit et supporte maintenant une bibliothèque utilisée par nos développeurs qui est utilisée pour valider les requêtes Oracle. Nous avons eu des tests qui ont confirmé que la requête correspondait à une valeur attendue, ce qui rend la casse importante (ce n'est pas dans Oracle) et approuve gaiement les requêtes non valides tant qu'elles correspondent complètement à la valeur attendue.

Au lieu de cela, ma bibliothèque analyse la requête à l'aide d'Antlr et d'une syntaxe Oracle 12c, puis encapsule diverses assertions sur l'arbre de syntaxe lui-même. Des choses comme, il est valide (aucune erreur d'analyse n'a été déclenchée), tous ses paramètres sont satisfaits par la collection de paramètres, toutes les colonnes attendues lues par le lecteur de données sont présentes dans la requête, etc. Tous ces éléments sont des éléments qui ont glissé jusqu'à production à différents moments.

Un de mes collègues ingénieurs m'a envoyé une requête lundi qui avait échoué (ou plutôt, avait réussi quand il aurait dû échouer) au cours du week-end. Ma bibliothèque a dit que la syntaxe était correcte, mais elle a explosé lorsque le serveur a essayé de l'exécuter. Et quand il a regardé la requête, il était évident pourquoi:

UPDATE my_table(
SET column_1 = 'MyValue'
WHERE id_column = 123;

J'ai chargé le projet et ajouté un test unitaire qui a affirmé que cette requête ne devrait pas être valide. De toute évidence, le test a échoué.

Ensuite, j'ai débogué le test ayant échoué, parcouru le code où je m'attendais à ce qu'il lève l'exception, et compris qu'Antlr était soulevant une erreur sur le paren ouvert, mais pas en quelque sorte le code précédent était attendu. J'ai modifié le code, vérifié que le test était maintenant vert (passant) et qu'aucun autre n'avait cassé le processus, commis et poussé.

Cela a pris peut-être 20 minutes, et au cours du processus, j'ai réellement amélioré la bibliothèque de manière significative car elle supportait maintenant une gamme entière d'erreurs qu'elle ignorait auparavant. Si je n'avais pas de tests unitaires pour la bibliothèque, la recherche et la résolution du problème auraient pu prendre des heures.

2
GalacticCowboy

Le but des tests automatisés est de vous dire quand vous avez cassé quelque chose le plus tôt possible. Le workflow ressemble un peu à ceci:

  1. Faire un changement
  2. Créez et testez votre changement (idéalement automatiquement)
  3. Si les tests échouent, cela signifie que vous avez cassé quelque chose qui fonctionnait auparavant
  4. si les tests réussissent, vous devez être sûr que votre changement n'a introduit aucune nouvelle régression (selon la couverture du test)

Si vos tests ont déjà échoué, alors l'étape 3 ne fonctionne pas aussi efficacement - les tests échoueront, mais vous ne savez pas si cela signifie que vous avez cassé quelque chose ou non sans enquêter. Peut-être pourriez-vous compter le nombre de tests ayant échoué, mais une modification peut alors corriger un bogue et en casser un autre, ou un test peut commencer à échouer pour une raison différente. Cela signifie que vous devez attendre un certain temps avant de savoir si quelque chose a été brisé, soit jusqu'à ce que tous les problèmes aient été résolus ou jusqu'à ce que chaque test ayant échoué ait été examiné.

La possibilité pour les tests unitaires de trouver les bogues nouvellement introduits le plus tôt possible est la chose la plus précieuse à propos des tests automatisés - plus un défaut n'est pas découvert plus il est coûteux de le réparer.

Il promeut l'idée que le code devrait être parfait et qu'aucun bogue ne devrait exister
Il est décourageant de penser à des tests unitaires qui échoueront

Les tests pour les choses qui ne fonctionnent pas ne vous disent rien - écrivez des tests unitaires pour les choses qui font fonctionnent, ou que vous êtes sur le point de réparer. Cela ne signifie pas que votre logiciel est exempt de défauts, cela signifie qu'aucun des défauts pour lesquels vous avez précédemment écrit des tests unitaires n'est revenu.

Il dissuade d'écrire des tests unitaires à l'avance

Si cela fonctionne pour vous, écrivez des tests à l'avance, ne les enregistrez pas dans votre master/trunk jusqu'à ce qu'ils passent.

Si, à un moment donné, tous les tests unitaires réussissent, il n'y a pas de vue d'ensemble de l'état du logiciel à un moment donné. Il n'y a pas de feuille de route/objectif.

Les tests unitaires ne sont pas destinés à définir une feuille de route/un objectif, peut-être utiliser un backlog pour cela à la place? Si tous vos tests réussissent, la "vue d'ensemble" est que votre logiciel n'est pas cassé (si votre couverture de test est bonne). Bien joué!

2
Justin

"mais à tout moment, tous les tests unitaires doivent réussir"

Si telle est l'attitude de votre entreprise, c'est un problème. À un certain moment, à savoir, lorsque nous déclarons que le code est prêt à passer à l'environnement suivant, tous les tests unitaires doivent réussir. Mais pendant le développement, nous devons systématiquement nous attendre à ce que de nombreux tests unitaires échouent.

Aucune personne raisonnable ne s'attend à ce qu'un programmeur obtienne son travail parfait du premier coup. Ce à quoi nous nous attendons raisonnablement, c'est qu'il continuera à y travailler jusqu'à ce qu'il n'y ait aucun problème connu.

"Il est décourageant de penser à des tests unitaires qui échoueront. Ou certainement de proposer des tests unitaires qui seraient difficiles à corriger." Si une personne de votre organisation pense qu’elle ne devrait pas mentionner un test possible car il pourrait échouer et lui demander plus de travail pour le réparer, cette personne n’est absolument pas qualifiée pour son travail. Il s'agit d'une attitude désastreuse. Voudriez-vous un médecin qui dit: "Quand je fais une chirurgie, je ne vérifie pas délibérément si les points sont corrects, parce que si je vois qu'ils ne le sont pas, je devrai y retourner et les refaire et que va ralentir la fin de l'opération "?

Si l'équipe est hostile aux programmeurs qui identifient les erreurs avant que le code ne passe en production, vous avez un vrai problème avec l'attitude de cette équipe. Si la direction punit les programmeurs qui identifient les erreurs qui ralentissent la livraison, il y a de fortes chances que votre entreprise se dirige vers la faillite.

Oui, il est certainement vrai que parfois des gens rationnels disent: "Nous approchons de la date limite, c'est un problème trivial et cela ne vaut pas la peine de consacrer les ressources nécessaires pour le réparer. Mais vous ne pouvez pas prendre cette décision de manière rationnelle si vous ne le savez pas. Examiner froidement une liste d'erreurs et attribuer des priorités et des calendriers pour les corriger est rationnel. Vous rendre délibérément ignorant des problèmes afin que vous n'ayez pas à prendre cette décision est stupide. Pensez-vous que le client ne le découvrira pas simplement parce que vous ne vouliez pas le savoir?

1
Jay

Un point que je ne pense pas ressort des réponses précédentes est qu'il y a une différence entre les tests internes et les tests externes (et je pense que de nombreux projets ne sont pas assez prudents pour distinguer les deux). Un test interne teste qu'un composant interne fonctionne comme il le devrait; un test externe montre que le système dans son ensemble fonctionne comme il le devrait. Il est tout à fait possible, bien sûr, d'avoir des défaillances dans les composants qui n'entraînent pas une défaillance du système (peut-être qu'il existe une caractéristique du composant que le système n'utilise pas, ou peut-être que le système récupère d'une défaillance du système). composant). Une défaillance de composant qui n'entraîne pas de défaillance du système ne devrait pas vous empêcher de publier.

J'ai vu des projets paralysés par trop de tests de composants internes. Chaque fois que vous essayez d'implémenter une amélioration des performances, vous interrompez des dizaines de tests, car vous modifiez le comportement des composants sans réellement modifier le comportement visible du système en externe. Cela conduit à un manque d'agilité dans le projet dans son ensemble. Je crois que l'investissement dans les tests de système externes a généralement un meilleur résultat que l'investissement dans les tests de composants internes, en particulier lorsque vous parlez de composants de très bas niveau.

Lorsque vous suggérez que l'échec des tests unitaires n'a pas vraiment d'importance, je me demande si c'est ce que vous avez en tête? Vous devriez peut-être évaluer la valeur des tests unitaires et abandonner ceux qui causent plus de problèmes qu'ils n'en valent, tout en vous concentrant davantage sur les tests qui vérifient le comportement visible de l'extérieur de l'application.

0
Michael Kay