Comment gérer un nombre énorme de tests qui échouent? [fermé]

22

Je travaille au développement d'un ancien projet écrit en Java. Nous avons plus de 10 millions de LOC et, pire encore, plus de 4000 tests fonctionnels.

Les tests, programmés par Hudson, échouent comme des fous à chaque changement de code plus important. La vérification de l'échec du test - s'il s'agit d'un problème dans le produit ou dans le test, prend des mois. Nous ne pouvons pas supprimer les anciens tests car nous ne savons pas ce qu'ils testent!

Ce que nous pouvons faire? Comment procéder avec autant de tests hérités?

Hector Brosuli
la source
6
Les vraies questions ont des réponses. Plutôt que d'expliquer pourquoi votre situation est terrible, ou pourquoi votre patron / collègue vous rend malheureux, expliquez ce que vous voulez faire pour l'améliorer. Pour plus d'informations, cliquez ici ...
gnat
13
Pourquoi avez-vous permis aux tests de commencer à échouer en premier lieu? BTW 4000 n'est pas autant de tests pour 10
MLOC
6
Arrêter de tomber et rouler.
Navin
13
Découvrez ce que les tests testent. Ensuite, revoyez et demandez-vous d'abord comment les tests sur terre prennent des mois pour trouver un problème, et découvrez également comment vos exigences ont tellement changé. Les tests sont destinés à encapsuler les exigences dans une application. Si vos tests échouent, votre code ne fonctionne pas conformément aux exigences - soit vous les avez mal écrites, soit aucun de vos codes ne respecte vos exigences.
Dan Pantry
6
Nous avons tous vu un compilateur éliminer un million d'erreurs à cause d'un seul '}' manquant. S'il s'agit de tests fonctionnels avec une pléthore de dépendances, peut-être que le même genre de problème est à l'œuvre?
Dan Pichelman

Réponses:

37

Abandonnez-les.

Je sais qu'il est difficile de laisser tomber quelque chose qui a clairement été beaucoup d'efforts à produire, mais les tests ne fonctionnent pas pour vous, ils travaillent contre vous. Une suite de tests est censée vous donner l'assurance que le système fait ce qu'il est censé faire. S'il ne le fait pas, c'est un passif au lieu d'un actif. Peu importe que le système ou les tests soient en cause - tant que l'exécution de la suite de tests signale d'énormes quantités d'erreurs, elle ne peut pas remplir son objectif.

Ce dont vous avez besoin maintenant, c'est d'une nouvelle suite de tests qui s'exécute sans erreur. Cela signifie qu'il aura initialement peu de couverture, en fait presque pas de couverture. Chaque fois que vous corrigez ou prenez le temps de bien comprendre quelque chose sur votre système, vous réordonnez ces connaissances dans un test. Au fil du temps, cela produira un nouveau filet de sécurité sur lequel vous pourrez vous appuyer à l'avenir. Essayer de réparer un vieux filet de sécurité mal compris est un temps qui ne vaut presque jamais la peine.

Je recommanderais même de ne pas transférer les tests de l'ancienne suite vers la nouvelle suite. Bien sûr, certains d'entre eux peuvent réussir maintenant, mais est-ce parce qu'ils testent exactement ce qu'ils sont censés tester, ou simplement parce que certains tirs aléatoires atteignent toujours la cible? Évidemment, vous devez être pragmatique sur ce qui peut et ne peut pas être fait avec l'effort que vous avez à dépenser, mais vous ne pouvez pas faire de compromis sur le principe qu'une suite de tests doit fonctionner correctement pour faire son travail .

Kilian Foth
la source
9
Je ne vois pas la logique de votre argument: "Une suite de tests est censée vous donner l'assurance que le système fait ce qu'il est censé faire. [...] Ce dont vous avez besoin maintenant, c'est d'une nouvelle suite de tests qui s'exécute sans les erreurs." Si vous avez un code défectueux qui fait échouer les tests, cela ne signifie pas que vous devez réécrire les tests afin que le code défectueux passe.
DBedrenko
13
La situation d'Hector est qu'il ne sait pas si le code ou les tests sont erronés . S'il le faisait, il pourrait travailler avec la base de code et changer parfois les tests, parfois le code d'entreprise. Dans l'état actuel des choses, même ce genre de travail pénible ne paierait pas, car vous ne savez pas si vous résolvez des problèmes ou si vous les perpétuez.
Kilian Foth
5
"Une suite de tests est censée vous donner l'assurance que le système fait ce qu'il devrait." Non, il est censé me dire si le système fait ce qu'il devrait; la fausse confiance est pire qu’aucune. "Ce dont vous avez besoin, c'est d'une suite de tests qui s'exécute sans erreur" Non, ce dont il a besoin, c'est d'une suite de tests qui lui donne des informations utiles sur la solidité du code. Ce qu'il a maintenant, c'est de nombreux voyants cryptiques, qui sont meilleurs qu'un feu vert d'une nouvelle suite de tests brillante qui ne teste rien. Il devrait désactiver temporairement les anciens tests , mais ne pas abandonner ceux qu'il n'a pas vérifiés comme faux.
Bêta
4
Cette réponse est un conseil incroyablement mauvais! Si des modifications de code plus petites entraînent un grand nombre d'échecs de tests, vous avez probablement des problèmes de qualité de code. Le test vous informera au moins que vous avez cassé quelque chose. Vous devez améliorer le code (en refactorisant soigneusement à l'aide de tests). Si vous supprimez simplement les tests, vous n'avez aucun moyen de savoir si vous cassez quelque chose.
JacquesB
4
C'est un conseil terrible. Si l'OP et son équipe ne peuvent déjà pas comprendre la base de code et ses tests, il est peu probable que lancer les tests et recommencer puisse résoudre le problème principal de l'OP - comprendre la base de code. Je pense que nous pouvons supposer que les tests ont fonctionné une fois écrits - donc son équipe doit rechercher ce que chaque test teste et lire la source pour déterminer si c'est la base de code ou le test qui est incorrect aujourd'hui. Bien plus simple que de recommencer avec des tests mal guidés et non informés / naïfs.
SnakeDoc
29

Allez corriger les tests.

Votre plus grande erreur est d'avoir laissé échouer les tests, et vous l'avez évidemment ignoré pendant un certain temps. Ce que vous avez n'est pas des "tests hérités" - vous travaillez sur un code hérité. Et je considère que tout code écrit sans test est hérité.


La vérification de l'échec du test - si c'est un problème dans le produit ou dans le test, prend des mois. Nous ne pouvons pas supprimer les anciens tests car nous ne savions pas ce qu'ils testent!

Il semble qu'il y ait un problème encore plus important dans votre organisation, car vous ne travaillez pas avec des exigences claires. Je ne peux pas comprendre que vous (ou quelqu'un d'autre) ne pouvez pas confirmer le bon comportement.

BЈовић
la source
4
C'est ce qui devrait idéalement être fait, mais il semble que les tests ici soient si mauvais que les programmeurs ne savent même pas ce qu'ils testent. Je pense que dans ce cas, il peut être préférable de se débarrasser des tests WTF et de commencer à en écrire de nouveaux et significatifs tout de suite! Dans un projet récent, j'ai eu un problème similaire avec un collègue dont les tests ont toujours échoué sans bonne raison (il n'a pas échoué parce que ce qui était censé être testé s'est mal passé, mais parce que le code du test était si fragile et même pas déterministe!) . J'ai passé des jours à réécrire ce que je pouvais et j'ai jeté le reste!
Shautieh
@Shautieh Les tests WTF ne vont pas sans le code WTF, donc la correction des tests signifie généralement une refactorisation du code. Et les tests qui échouent au hasard sont le signe de l'incompétence. Et le superviseur de votre collègue est à blâmer pour ne pas avoir fait son travail.
BЈовић
2
Parfois, la vie est dure: le gars responsable des tests WTF (et du code) a gagné le salaire le plus élevé de l'équipe (20 +% de plus que moi), et quand il a quitté au milieu du projet (parce qu'il a trouvé un travail mieux rémunéré) ) J'ai dû affronter certains de ses développeurs: / Mais vous avez tout à fait raison de dire que notre superviseur était aussi à blâmer ^^
Shautieh
@Shautieh: un de mes collègues a dit un jour qu'un bogue dans le code était deux bogues: un bogue dans le code et un angle mort dans les tests. Je suppose que c'est en fait trois si vous comptez le développeur qui tolère les tests qui échouent, et quatre si vous comptez les gestionnaires qui promeuvent un tel incompétent.
Beta
@Beta Sonne assez similaire à la définition parfois utilisée dans TDD: "Un bug est un test que vous n'avez pas encore écrit."
Rétablir Monica du
22

Les tests sont précieux. À tout le moins, ils enregistrent que quelqu'un considérait qu'ils devraient passer du temps à les écrire, donc ils ont vraisemblablement eu une certaine valeur pour quelqu'un une fois. Avec de la chance, ils contiendront un enregistrement complet de toutes les fonctionnalités et bogues sur lesquels l'équipe a jamais travaillé - bien qu'ils puissent également être un moyen d'atteindre un certain nombre arbitraire de couverture de test sans être soigneusement réfléchi. Jusqu'à ce que vous les regardiez, vous ne saurez pas quel est le cas ici.

Si la plupart de vos tests réussissent la plupart du temps, alors mordez la balle et investissez du temps à déterminer ce que les quelques tests qui échouent tentaient de faire, et à les corriger ou à les améliorer pour que le travail soit plus facile la prochaine fois. Dans ce cas, passez à la section Déterminer l'intention de chaque test pour obtenir des conseils sur la procédure à suivre avec un petit nombre de tests ayant échoué.

D'un autre côté, vous pouvez être confronté à une version rouge maintenant, et à des centaines voire des milliers de tests qui n'ont pas réussi depuis un certain temps, et Jenkins n'a pas été vert depuis longtemps. À ce stade, l'état de la génération Jenkins est devenu inutile et un indicateur clé de problèmes avec votre enregistrement ne fonctionne plus. Vous devez résoudre ce problème, mais vous ne pouvez pas vous permettre d'arrêter tout progrès vers l'avant pendant que vous nettoyez le gâchis dans votre salon.

Afin de garder votre santé mentale tout en effectuant l'archéologie requise pour déterminer quelle valeur peut être récupérée à partir des tests qui ont échoué, je recommanderais les étapes suivantes:

Désactivez temporairement les tests ayant échoué.

Il y a plusieurs façons de le faire, en fonction de votre environnement, que vous ne décrivez pas clairement, donc je ne peux vraiment pas en recommander un en particulier.

Certains cadres prennent en charge la notion de défaillances attendues. Si le vôtre le fait, c'est parfait, car vous verrez un compte à rebours du nombre de tests restant dans cette catégorie, et vous serez même informé si certains d'entre eux commencent à passer de manière inattendue.

Certains frameworks prennent en charge les groupes de tests et vous permettent d'indiquer à Hudson uniquement d'exécuter certains des tests ou d'ignorer un groupe de tests. Cela signifie que vous pouvez occasionnellement exécuter le groupe de tests manuellement pour voir si certains réussissent.

Certains frameworks vous permettent d'annoter ou de marquer autrement des tests uniques à ignorer. Dans ce cas, il est plus difficile de les gérer en groupe, mais cela les empêche de vous distraire.

Vous pouvez déplacer les tests vers une arborescence source qui n'est normalement pas incluse dans la génération.

In extremis, vous pouvez supprimer le code de la TETE du système de contrôle de version, mais cela rendra plus difficile la reconnaissance de la fin de la troisième phase.

L'objectif est d'amener Jenkins à passer au vert dès que possible, afin que vous puissiez commencer à vous déplacer dans la bonne direction dès que possible.

Gardez les tests pertinents.

Décidez d'ajouter de nouveaux tests lorsque vous ajoutez ou modifiez du code, et engagez-vous à conserver tous les tests réussis.

Les tests peuvent échouer pour diverses raisons, notamment le fait qu'ils n'étaient pas des tests bien écrits au départ. Mais une fois que vous avez obtenu Jenkins vert, il est très important de le garder ainsi.

Habituez-vous à écrire de bons tests et faites-en un gros problème si les tests échouent.

Déterminez l'intention de chaque test.

Passez les tests désactivés un par un. Commencez par ceux qui affectent les modules que vous modifiez le plus fréquemment. Déterminez l'intention du test et la raison de l'échec.

  • Teste-t-il une fonctionnalité qui a été supprimée de la base de code exprès? Ensuite, vous pouvez probablement le supprimer.

  • Attrape-t-il un bug que personne n'a encore remarqué? Rétablissez le test et corrigez le bogue.

  • Est-ce qu'il échoue parce qu'il faisait des hypothèses injustifiées (par exemple, en supposant que le texte du bouton serait toujours en anglais, mais maintenant vous avez localisé votre application pour plusieurs langues)? Ensuite, déterminez comment concentrer le test sur une seule chose et isoler le mieux possible des modifications non liées.

  • Le test s'étend-il à l'ensemble de l'application et représente-t-il un test système? Ensuite, supprimez-le de votre suite de tests Jenkins principale et ajoutez-le à la suite de régression qui s'exécute moins fréquemment.

  • L'architecture de l'application a-t-elle changé au-delà de la reconnaissance, de sorte que le test n'a plus rien d'utile? Supprime-le.

  • Le test a-t-il été ajouté pour augmenter artificiellement les statistiques de couverture du code, mais ne fait-il rien de plus que confirmer que le code se compile correctement et ne passe pas dans une boucle infinie? Ou bien, le test confirme simplement que le cadre de simulation sélectionné renvoie les résultats auxquels vous venez de le dire? Supprime-le.

En conséquence, certains tests seront maintenus, certains seront modifiés, certains seront divisés en plusieurs morceaux indépendants de la taille d'une bouchée, et certains seront supprimés. Tant que vous progressez avec de nouvelles exigences, réserver un peu de temps pour faire face à une dette technique comme celle-ci est la chose responsable à faire.

Bill Michell
la source
1
C'est vraiment une très mauvaise idée de désactiver les tests simplement parce qu'ils échouent! Le reste de vos conseils est bon, mais pas ça. Les tests que vous ne comprenez pas ne doivent jamais être désactivés. Le but du test n'est pas d'obtenir une barre verte, mais de faire fonctionner un logiciel!
JacquesB
Cela dépend de l'ampleur du problème. Mais je suis d'accord, je n'ai pas vraiment précisé cela.
Bill Michell
Ajout d'un paragraphe pour faire la distinction entre "nous sommes verts mais chaque changement fait que les choses deviennent rouges" et "nous sommes rouges depuis si longtemps, nous avons oublié à quoi ressemble le vert"
Bill Michell
Au lieu de désactiver ou même de supprimer le test, certains frameworks fournissent également la notion d' échec attendu . Cela pourrait aider à augmenter le SNR parce que vous serez plus directement alerté des nouveaux échecs (ce que vous ne ferez pas s'il y a toujours un grand nombre d'échecs) mais tout de même être informé des échecs connus et - peut-être encore plus important - lorsqu'un le test qui échouait auparavant repasse soudainement. Si les échecs inattendus sont lus et les échecs attendus en orange, faites en sorte que les tests rouges verts soient vos premiers et que les orange verts deviennent votre deuxième priorité.
5gon12eder
11

4000 tests est un problème insoluble. 40 tests est plus maniable. Sélectionnez au hasard un nombre gérable de tests à exécuter et à analyser. Classez les résultats comme:

  1. Test inutile
  2. Test utile qui fonctionne correctement
  3. Test utile qui échoue

Si de nombreux tests entrent dans la première catégorie, il est peut-être temps de jeter votre suite de tests actuelle et d'en créer une utile pour le code actuel.

Si de nombreux tests échouent de manière à vous signaler un problème dans votre code, vous devez passer par les tests qui échouent en corrigeant les problèmes. Vous pouvez constater que la correction d'un ou deux bogues fait exécuter un grand nombre de tests.

Patricia Shanahan
la source
2
+ (int) (PI / 3) pour avoir fourni un moyen réel et simple de tester la suite de tests - bien que je convienne que, en règle générale, les tests tels que décrits par OP sont le signe d'une conception défectueuse - mais sans tester ce qui ne va pas, tout conseil sur la suite de tests elle-même (que ce soit "abandonnez-les", "corrigez les tests", "écrivez de nouveaux tests") est tout simplement inutile. Exactement comme vous le dites: si j'avais des tests 4k et que 40 complètement aléatoires de ces 3/4 sont minables et inutiles - je n'hésiterais pas à vider toute la suite. Si 3/4 de ceux-ci auraient été réellement utiles - je les laisserais et me concentrerais sur l'amélioration du code.
vaxquis
7

Si cette affirmation est vraie,

Les tests ... échouent comme des fous à chaque changement de code plus important.

cela implique alors que si vous restaurez le code juste avant un "changement de code plus important", de nombreux tests passeront à nouveau. Après cela, récupérez un plus petit bloc de modifications et voyez quels tests échouent récemment. Cela vous aidera à mieux isoler les modifications de code qui provoquent l'échec des tests. Pour chaque test, une fois que vous avez isolé le problème, vous devriez alors pouvoir déterminer si le nouveau code était défectueux ou si le test l'était. S'il s'agit d'un problème avec le nouveau code, assurez-vous de le comparer avec la dernière version au cas où ce bogue particulier aurait déjà été corrigé.

Répétez jusqu'à ce que vous ayez la dernière base de code.

Cela peut sembler une tâche écrasante, mais il est très probable qu'une fois que vous emprunterez ce chemin et commencerez à isoler certains des problèmes, un schéma commencera à émerger, ce qui pourrait accélérer considérablement le processus. Par exemple:

  • Vous remarquerez peut-être que de nombreux tests dépendent de quelque chose d'autre qui est défectueux. Réparer cette pièce peut corriger de nombreux tests.
  • Vous pouvez remarquer que de nombreux tests sont défectueux et doivent être corrigés ou supprimés.
  • Vous remarquerez peut-être qu'un développeur particulier a une fréquence beaucoup plus élevée de rupture des tests. Ce développeur peut avoir besoin de plus de formation ou de supervision.
TTT
la source
3

Si vous ne savez pas ce qu'ils testent, supprimez-les jusqu'à ce que vous le sachiez. Les tests sont des choses fluides, si vous supprimez une fonctionnalité qui n'est plus nécessaire, vous devez vous attendre à devoir changer le test qui teste cette fonctionnalité! Donc, à moins que vous ne sachiez ce que les tests testent, vous n'avez aucun espoir de changer la base de code avec eux en place.

Vous pouvez configurer le système de test sur les machines du développeur et y exécuter afin que les développeurs puissent voir avec quelles parties les tests interagissent, avec un peu de chance, fournir cette documentation manquante et se familiariser avec la base de code que vous ne modifiez pas correctement, ou non test plus longtemps correctement.

En bref - si vos anciens tests échouent lorsque vous effectuez des modifications, vos modifications de code ne sont pas bonnes. Utilisez ces tests comme moyen d'éducation sur le fonctionnement du système.

gbjbaanb
la source
1
C'est pourquoi j'aime l' @Ignoreannotation de JUnit - vous pouvez conserver vos tests, mais pas les exécuter. Il s'agit alors simplement de les réactiver et de les réparer un par un. Il vous permet de réduire votre concentration à une poignée de tests à la fois, au lieu d'être submergé par des milliers d'échecs.
TMN
1
C'est un mauvais conseil. Vous ne devez pas supprimer ou désactiver un test que vous ne comprenez pas. Seulement si vous faites comprendre le test, et vous êtes confiant , il teste une fonctionnalité obsolète, doit - il être désactivé ou supprimé.
JacquesB
2

La chose la plus importante que je ferais serait de revenir aux principes fondamentaux de ce que les tests sont censés faire et de ce que l'entreprise doit continuer de faire. Le travail de test consiste à identifier les problèmes avant qu'ils ne deviennent coûteux à résoudre plus tard. Je pense que le mot clé de cette phrase est "cher". Ces problèmes nécessitent une solution commerciale. Des problèmes coûteux apparaissent-ils sur le terrain? Si c'est le cas, les tests échouent carrément.

Votre gestion et vous devez vous mettre à l'épreuve de la réalité. Vous constatez que les coûts de développement montent en flèche en raison d'un ensemble de tests hérités. Comment ces coûts se comparent-ils aux coûts de livraison d'un produit défectueux parce que vous avez désactivé les tests? Comment se comparent-ils à la lourde tâche de déterminer réellement les comportements dont les utilisateurs ont besoin (quelles sont les choses qui devraient être testées)?

Ce sont des problèmes qui nécessitent des solutions commerciales car ils touchent le côté commercial du travail. Vous livrez un produit à un client, et c'est une frontière qui intéresse beaucoup les entreprises. Ils peuvent être en mesure d'identifier des solutions que vous, en tant que développeur, ne pouvez pas. Par exemple, il peut être raisonnable pour eux de fournir deux produits: un produit "hérité" pour ceux qui ont besoin de fiabilité et qui sont prêts à renoncer à de nouvelles fonctionnalités, avec un produit "visionnaire" qui peut avoir plus de défauts, mais qui ouvre la voie. Cela vous donnerait l'occasion de développer deux ensembles de tests indépendants ... un héritage avec 4000 tests, et un avec plus de tests que vous pensez devoir faire (et les documenter pour que ce processus ne se répète pas).

Ensuite, l'art commence: comment pouvez-vous gérer cette bête à deux têtes afin que les avancées dans une branche aident également l'autre branche? Comment vos mises à jour de la branche "visonary" peuvent-elles refluer vers la branche "legacy", malgré des exigences de test rigides. Comment les demandes continues des clients sur la branche "héritée" peuvent-elles mieux façonner votre compréhension des exigences dont vos anciens clients auraient besoin si vous finissiez par fusionner les produits?

Cort Ammon - Rétablir Monica
la source
-3

Nous ne pouvons pas supprimer les anciens tests car nous ne savions pas ce qu'ils testent!

C'est exactement pourquoi vous devriez supprimer les anciens tests! Si vous ne savez pas ce qu'ils font, l'échec n'a aucun sens et les exécuter est une perte de temps. Jetez-les et recommencez.

Mohair
la source
2
cela semble simplement répéter le point déjà fait et expliqué dans la réponse du haut
gnat
4
L'échec n'est pas «dénué de sens», cela signifie que vous ne comprenez pas le système aussi bien que vous le pensiez.
Ben Voigt
L'échec n'a absolument aucun sens ici, car le PO a clairement déclaré qu'il ne comprenait pas le système.
Mohair