Alternative à l'indicateur «Passing / Broken build»?

14

Lorsqu'une intégration continue exécute les tests à chaque validation, une meilleure pratique courante consiste à faire passer tous les tests à tout moment (c'est-à-dire "ne pas interrompre la construction").

Je trouve quelques problèmes avec ça:

Par exemple, on ne peut pas aider un projet open source en créant des tests correspondant aux tickets. Je sais que si je propose une Pull Request à un projet open source contenant un test ayant échoué, la build sera marquée comme ayant échoué et le projet ne voudra pas que cela soit fusionné dans son référentiel car cela "casserait la build".

Et je ne pense pas que ce soit une mauvaise chose d'avoir des tests qui échouent dans votre dépôt , c'est comme avoir des problèmes ouverts dans votre tracker. Ce ne sont que des choses qui attendent d'être corrigées.

Il en va de même dans une entreprise. Si vous travaillez avec TDD, vous ne pouvez pas écrire de tests, valider puis écrire le code logique qui remplit le test. Cela signifie que si j'ai écrit 4 à 5 tests sur mon ordinateur portable, je ne peux pas les valider avant de partir en vacances. Personne ne peut reprendre mon travail. Je ne peux même pas les "partager" avec un collègue sauf en les envoyant par mail par exemple. Cela empêche également de travailler avec une personne écrivant les tests, l'autre écrivant le modèle.

Tout cela pour dire: suis-je en train de mal utiliser / mal comprendre le processus de construction / l'intégration continue? Il me semble que «passer» / «ne pas passer» est un indicateur trop étroit.

Existe-t-il un moyen de rendre l'intégration continue et compatible TDD?

Peut-être existe-t-il une solution / pratique standard pour distinguer les "nouveaux tests" (qui peuvent échouer) et les "tests de régression" (qui ne devraient pas échouer car ils fonctionnaient auparavant)?

Matthieu Napoli
la source
1
Avoir un indicateur qui indique si le nombre de tests ayant échoué a augmenté (rouge) ou diminué (vert) au cours de la dernière heure (ou plus).
Joachim Sauer
2
Je ne suis pas un spécialiste TDD / ALM (d'où le commentaire, plutôt qu'une réponse) mais je pense que votre problème peut être résolu avec des branches privées / branches de fonctionnalités. Travaillez-vous sur la fonctionnalité A? Branchez-le, travaillez sur la branche (avec des collègues), et une fois que vous avez terminé - fusionnez-le dans le coffre continuellement intégré.
Avner Shahar-Kashtan
@JoachimSauer Oui, mais cette métrique est-elle normalisée / utilisée dans un grand projet? J'essaie de comprendre pourquoi la plupart des projets (et des outils CI) fonctionnent de cette façon.
Matthieu Napoli
Je pense que le critère correct pour "les tests qui peuvent échouer" n'est pas les "nouveaux tests", mais plutôt les "tests pour les problèmes ouverts connus". Je peux voir à quel point ces tests sont utiles - je peux aussi voir comment ces tests NE SONT PAS utiles dans la construction CI car ils polluent la signification du test réussissent / échouent là-bas (vous voulez seulement exécuter des tests pour lesquels quelqu'un a réellement passé du temps pour les faire passer).
Joris Timmermans
@MadKeithV Exactly
Matthieu Napoli

Réponses:

12

Je vois où vous en êtes, mais ces types de problèmes sont généralement résolus par d'autres moyens. Il y a une bonne raison pour laquelle il s'agit d'un protocole standard. Si quelqu'un soumet du code qui ne compile pas, tout le monde qui met à jour son code aura un programme qui ne compile pas . Cela inclut les programmeurs qui travaillent actuellement sur quelque chose de complètement différent et se retrouvent en quelque sorte dans une situation dans laquelle ils doivent attendre avant de pouvoir compiler et tester ce sur quoi ils travaillent.

Le protocole standard est que vous pouvez valider des modifications même pour un travail complet ou même incomplet, tant qu'il compile afin que les programmeurs puissent mettre à jour leur code chaque jour si nécessaire.

Cependant, je vois toujours où vous en êtes. Parfois, vous souhaitez vous engager afin de simplement enregistrer votre code. Pour cela, la plupart des référentiels sources prennent en charge la ramification. Cela vous permet de créer une branche privée, de travailler dessus sans déranger les autres, puis de fusionner dans le coffre lorsque le travail est terminé. Cela vous permet de valider quand vous le souhaitez sans aucun jeu associé à la rupture de la génération.

Si cela ne vous convient pas, GIT vous permet de valider (pousser) vers des référentiels sur votre machine locale, mais le référentiel pourrait être n'importe où. Vous pouvez créer un référentiel pour le travail potentiellement partiel / incomplet et un autre référentiel pour le travail terminé, et sur ce référentiel, vous pouvez ajouter une construction nocturne.

Encore une fois, je ne saurais trop insister sur l'importance. Ne commettez jamais de code cassé dans le tronc! Vos contributions ne peuvent pas affecter le travail des autres programmeurs.

Éditer

Je vois que vous vouliez des tests brisés, mais à mon humble avis, il y a peu de différence. L'intérêt d'un test est de déterminer si un aspect particulier d'un programme réussit ou échoue. S'il échoue toujours et que vous ne faites rien, alors le test, dans l'utilisation traditionnelle des tests unitaires, ne sert à rien. Si vous l'utilisez pour effectuer une autre métrique qui n'implique pas nécessairement une validation «échouée» si l'un de ces tests échoue, je vous recommande fortement de trouver une autre façon de faire la même chose.

Sinon, vous risquez que le test ne soit jamais pris en considération ou s'il entraîne l'échec de votre build, que vos collègues programmeurs ignorent les builds ayant échoué. Il est plus important que les programmeurs sachent quand ils ont cassé une build que d'effectuer un test qui n'offre pas de réel aperçu et ne peut entraîner que de mauvaises pratiques.

Neil
la source
1
En effet, vous faites un point avec les branches thématiques. Mais je ne parle jamais de commettre du code cassé, juste d'échouer aux tests. Par exemple, je pourrais aider un projet open source en créant des tests pour les tickets entrants même si je ne sais pas comment les corriger. Cela fait gagner du temps aux mainteneurs.
Matthieu Napoli
Si je travaille sur le même projet que vous et que vous téléchargez un test qui échoue, j'ai maintenant une version qui a un test qui échoue. Je pourrais finir par supprimer votre test, car cette fonctionnalité n'est pas encore implémentée, ou décider d'implémenter la fonctionnalité et finir par piétiner votre code et perdre du temps. S'il y avait une culture de faire cela, ce genre de réponse pourrait être évité, mais alors tout le monde le ferait, et donc même lorsque tous vos tests réussissent, pas tous les miens. Dans ce cas, la génération aura toujours des tests qui échouent. Je ne vois aucun avantage.
Michael Shaw
the build would always have failing testsprécisément! Mais est-ce une si mauvaise chose? Notre seule métrique est "la construction est cassée ou non", mais votre code pourrait être plein de bogues connus , donc cela ne veut rien dire sauf qu'il n'y a pas de régression. Dans un monde parfait, chaque problème de tracker aurait un test (la reproduction est plus facile que la correction). Donc, l'avantage serait de voir que 35 tests / 70% de tous les tests sont réussis, que la branche-A l'améliore à 40 tests (80%) sans régression, et que la branche-B a des régressions. Aujourd'hui, vous pouvez seulement dire que le Maître et la Branche-A sont OK et que la Branche-B est cassée.
Matthieu Napoli
@Matthieu Je vois où tu veux en venir. On dirait que vous avez besoin d'une catégorie spéciale ou de quelque chose qui dit "hé, si ce test échoue, c'est ok. Nous le savons. Mais nous voulons toujours qu'il s'exécute et s'il passe, c'est encore mieux et nous devrions supprimer la catégorie spéciale parce que nous nous soucions maintenant s'il se casse "
Earlz
@Earlz Exactement! Ce que je me demande, c'est "est-ce fait par quelqu'un, quelque part? Et y a-t-il des outils qui prennent en charge cela (CI et bibliothèques de tests unitaires?" échouer de toute façon et je ne verrai pas de différence entre les tests qui ont échoué, et donc ça ne sera pas utile: /
Matthieu Napoli
4

Étant donné une branche principale avec des tests qui échouent, comment pouvez-vous être sûr - sans comparer cette liste avec les versions précédentes - que vous n'avez pas introduit de bogues?

Le simple suivi du nombre de tests ayant échoué est insuffisant: vous pouvez corriger un test et en casser un autre. Et si vous êtes en vacances, les autres ne verront pas clairement la construction défaillante.

Gardez votre branche principale propre et verte en tout temps. Travailler dans une succursale. Gardez la branche sous CI, dans un travail distinct, et échouez aux tests selon votre cœur. Ne brisez pas le maître.

Demandez au réviseur de la branche de fusionner votre branche uniquement si elle réussit tous les tests. (Plus fortement: le réviseur ne peut fusionner votre branche que si le résultat de la fusion de la branche en maître passe tous les tests!)

Frank Shearar
la source
2
Simply tracking the number of failing tests is insufficientce n'est pas la seule métrique possible. Par exemple: Branch-A improves it to 40 tests (80% passing) with no regression. Aucune régression signifie que les tests réussis auparavant réussissent toujours. En bref, un test serait autorisé à échouer tant qu'il n'aurait jamais réussi. Il me semble que nous manquons de bonnes choses en contraignant à interdire les tests qui échouent dans les branches principales. (bien sûr cela nécessiterait des outils fonctionnant différemment: tests unitaires, CI, ...)
Matthieu Napoli
Je reste fidèle à mon argument: le maître doit toujours être vert, car il est clair et sans ambiguïté. Dans tous les cas, les tests de marqueur échouent ... dans une branche de fonctionnalité. CI peut continuer à rappeler aux gens les bogues en suspens.
Frank Shearar
Je pense que Matthieu propose une définition légèrement différente du "vert", ne s'écartant pas du fait que le maître soit toujours vert. Ce n'est pas évident pour moi que cela n'a pas de sens - il aurait besoin d'un outillage pas tout à fait trivial pour le suivi, bien sûr. (Besoin d'annuler un changement qui a fait passer ce test? Pas de chance si cela signifie que l'état est soudainement rouge ...)
Christopher Creutzig
NUnit a le concept d'un test ignoré. Cela pourrait être une alternative: ils ne sont pas exécutés, donc ils n'échouent pas, mais ils sont toujours signalés comme étant ignorés.
Frank Shearar
2

Il existe des moyens de résoudre vos problèmes sans abandonner les pratiques bien comprises et acceptées en matière d'intégration continue.

Je vais commencer par le problème de commettre un «test cassé» qui correspond à un ticket. Une solution consiste à créer un ou plusieurs tests de rupture exposant le problème, puis à résoudre le problème , afin qu'ils puissent être fusionnés à nouveau avec la ligne de code principale. La deuxième solution consiste à avoir les tests interrompus, mais utilisez un certain type d'indicateur ignore pour qu'ils ne s'exécutent pas et ne cassent pas la génération. Ajoutez éventuellement un commentaire ou une annotation spéciale qui rend très évident qu'il s'agit d'un test cassé pour Ticket#N. Joignez également une note au ticket lui-même qui fait référence aux tests créés qui attendent d'être ignorés et exécutés . Cela aiderait une personne à fixer le ticket, mais ne serait pas non plus un drapeau rouge pour quelqu'un qui tombe sur le test.

Et sur votre prochain problème avec TDD. TDD consiste à écrire un petit test, puis à écrire un petit morceau de code pour réussir ce test . Continuez ensuite à itérer jusqu'à ce que vous ayez un petit module fonctionnel. Je pense que si vous passez 4 ou 5 tests, vous partez en vacances, vous vous trompez peut-être. Vous pouvez coupler le programme avec quelqu'un de manière à ce que l'un d'entre vous écrive le test, l'autre le code correspondant. Vous ne devez cependant pas utiliser le référentiel de ligne de code principal pour partager ce code entre vous deux avant qu'un module terminé ne soit prêt à être validé. Comme d'autres l'ont suggéré, une branche partagée résoudrait vos problèmes là-bas.

Essayer de briser le mantra de l'intégration continue peut conduire à des chemins inattendus et effrayants. Par exemple, que signifierait la couverture du code dans ce type d'environnement ? Comment les développeurs ne ressentiraient-ils pas que le système possède de nombreuses " fenêtres cassées " ? Comment pourrait-on faire un changement, exécuter le test et savoir s'ils cassent réellement quelque chose de nouveau, ou ce sont juste les vieux trucs?

c_maker
la source
Vous n'avez pas besoin d'outils pour partager avec la personne avec qui vous programmez en binôme - remettez simplement le clavier. Si vous utilisez différents ordinateurs, eh bien, il n'y a rien de mal à cela, ce n'est tout simplement pas la "programmation par paires".
Christopher Creutzig
1

Je pense que votre problème fondamental est que vous incluez les RÉSULTATS des tests dans le cadre de la construction. Bien que certaines personnes soient évidemment d'accord avec vous, d'autres ne le sont pas. La rupture de la génération se produit lorsqu'elle ne se génère pas. Pas quand il ne se construit pas sans erreurs.

Considérez un projet majeur comme Windows ou Linux, ou même quelque chose comme Firefox - pensez-vous qu'ils sont livrés sans bug? Bien sûr que non. Maintenant, ces projets ne font pas TDD, mais ce n'est vraiment pas pertinent - TDD ne change pas deux faits fondamentaux: les bogues existent et il faut du temps pour les corriger. Temps qu'un projet (open source ou non) ne peut tout simplement pas se permettre de perdre sur des bogues de faible priorité. KDE a récemment corrigé un bogue vieux de plus d'une décennie. À quand remonte la dernière fois que vous avez entendu quelqu'un dire "Je suis content que nous ayons attendu une décennie pour expédier notre projet"?

TDD, en quelque sorte, rend probablement plus facile à expédier avec des bugs - parce que vous avez une meilleure compréhension de ce qu'est la faille. Si vous pouvez définir précisément la cause du bogue, vous disposez d'une excellente base pour évaluer le coût de sa correction.

Ma recommandation est de trouver un projet qui ne dérange pas le rouge parmi le vert.

jmoreno
la source
1
 > a common best practice is to have all the tests passing (green) at all times.

Je préfère que tous les tests n'échouent pas (pas en rouge).

Avec cette définition légèrement différente, vous pouvez également définir des tests qui sont

  • pas encore implémenté (gris dans nunit s'il y a une exception NotImplementedException)
  • connu pour échouer = "a besoin de faire" en marquant / annotant le test comme ignoré (jaune)

Si vous les archivez dans le référentiel, votre build continu n'est pas cassé et donc valide.

k3b
la source
0

Vous pouvez envisager deux "concepts" de construction de CI différents.

  1. Builds CI réguliers. La construction régulière de CI doit compiler et exécuter uniquement les tests pour lesquels du code a été écrit pour les faire passer, de sorte que les rapports CI de réussite / échec de test soient un indicateur clair et non ambigu de régression par rapport à l'état précédemment accepté du code.
  2. Un "futur" build CI. Cette version compile et exécute uniquement les tests pour lesquels aucun code n'a été écrit spécifiquement pour les faire passer. Il peut y avoir plusieurs raisons d'avoir un tel test:

    • Des tests peuvent être ajoutés pour des cas d'échec spécifiques à partir du suivi des problèmes, pour lesquels aucune correction n'a encore été tentée. Il est clairement utile d'avoir déjà un test codifié et en cours d'exécution pour un problème, même sans correction.

    • Tests ajoutés pour les nouvelles fonctionnalités requises qui n'ont pas encore été implémentées.

    • Il est souvent plus facile de savoir comment tester une défaillance ou une fonctionnalité que de savoir comment implémenter la fonctionnalité ou le correctif, et séparer les deux étapes en validant le test au contrôle de code source peut être utile pour garantir qu'aucune information n'est perdue.

Comme dans le CI "standard", dans le régime de développement régulier, l'équipe ne se pencherait que sur les résultats quotidiens de la génération régulière.

L'équipe peut également garder un œil sur l'évolution des cas de test à partir de la "future" version de CI - en particulier pour voir si des modifications apportées au CI régulier résolvent réellement les problèmes de la "future" version, ce qui peut être une indication d'une importante problème sous-jacent ou amélioration de la conception.

Enfin, si un membre de l'équipe dispose de plus de temps, il pourrait envisager de résoudre l'un des problèmes du "futur", et s'il réussit à le migrer vers "régulier" (tout en mettant à jour le statut du suivi des problèmes).

Joris Timmermans
la source
0

Et je ne pense pas que ce soit une mauvaise chose d'avoir des tests qui échouent dans votre dépôt, c'est comme avoir des problèmes ouverts dans votre tracker. Ce ne sont que des choses qui attendent d'être corrigées.

Le problème n'est pas l'échec des tests, c'est un indicateur de régression simple et sans contexte. Aka: en tant que développeur, puis-je vérifier un seul indicateur et savoir si j'ai introduit une régression ou un code de rupture.

Au moment où vous introduisez le concept de défaillances `` douces '' (ça va, nous y travaillons / pas encore implémenté / nous attendons la nouvelle version / ça va repasser une fois que cette autre build sera corrigée), vous aurez besoin tous ceux qui pourraient exécuter ou regarder le test pour connaître l'état attendu. Ce qui dans une équipe assez grande va changer d'heure en heure: votre indicateur devient vide de sens. Dans un contexte plus restreint (test d'intégration privée d'équipe par exemple), alors je pense que c'est comme une dette technique et c'est correct - il suffit de le gérer.

La façon dont la plupart des outils abordent le fait que «vert / passant» reflète le résultat attendu - pas que le code fonctionne:

  • La condition physique a le concept d'échec attendu.
  • JUnit a @Ignored / @Test (attendez =)
  • Le concombre a le statut `` pas encore implémenté '' (ou quelque chose comme ça)

Ceux-ci viennent avec leurs propres problèmes (comment distinguez-vous entre «oui, nous savons qu'il est cassé, en travaillant dessus» et «le comportement correct est une exception») - mais ils aident.

ptyx
la source
0

J'utilise des tests ignorés.

Dans le cadre de test unitaire particulier que j'utilise, je peux déclencher une exception SkipTest. Le test n'est pas réellement exécuté et son échec ne cassera pas la construction. Cependant, je peux voir le nombre de tests ignorés et voir s'il y a du travail à faire dans ce domaine.

Winston Ewert
la source