D'un point de vue technique, il est possible d'ajouter des points d'ancrage pré / post-push qui exécuteront des tests unitaires avant de permettre à une validation spécifique d'être fusionnée à une branche distante par défaut.
Ma question est la suivante: est-il préférable de conserver les tests unitaires dans le pipeline de construction (donc, en introduisant des commits cassés dans le repo) ou de ne pas autoriser de "mauvais" commits.
Je me rends compte que je ne suis pas limité à ces deux options. Par exemple, je peux autoriser tous les validations vers des branches et des tests avant de pousser les validations de fusion vers un référentiel. Mais si vous devez choisir entre exactement ces deux solutions, laquelle choisir et pour quelles raisons?
Réponses:
Non, ce n'est pas pour deux raisons:
La vitesse
Les commits devraient être rapides. Un commit qui prend 500 ms, par exemple, est trop lent et encouragera les développeurs à s’engager avec plus de parcimonie. Etant donné que sur tout projet plus grand qu'un Hello World, vous aurez des dizaines ou des centaines de tests, leur exécution prendra trop de temps avant la validation.
Bien entendu, les choses s'aggravent pour les projets plus volumineux avec des milliers de tests exécutés à la minute près sur une architecture distribuée, ou des semaines ou des mois sur une seule machine.
Le pire, c'est qu'il n'y a pas grand chose que vous puissiez faire pour accélérer les choses. De petits projets Python comportant, par exemple, cent tests unitaires, prennent au moins une seconde pour s'exécuter sur un serveur moyen, mais souvent beaucoup plus longtemps. Pour une application C #, il faudra en moyenne quatre à cinq secondes, en raison du temps de compilation.
À partir de ce moment, vous pouvez soit payer 10 000 $ de plus pour un meilleur serveur, ce qui réduira le temps, mais pas beaucoup, ou exécuter des tests sur plusieurs serveurs, ce qui ne fera que ralentir les choses.
Tous deux paient bien lorsque vous avez des milliers de tests (ainsi que des tests fonctionnels, de système et d'intégration), ce qui vous permet de les exécuter en quelques minutes au lieu de plusieurs semaines, mais cela ne vous aidera pas pour des projets de petite envergure.
Ce que vous pouvez faire, au lieu de cela, est de:
Encouragez les développeurs à exécuter des tests étroitement liés au code qu'ils ont modifié localement avant d'effectuer une validation. Ils ne peuvent peut-être pas exécuter des milliers de tests unitaires, mais ils peuvent en exécuter cinq ou dix.
Assurez-vous que trouver des tests pertinents et les exécuter est en fait facile (et rapide). Visual Studio, par exemple, est capable de détecter les tests susceptibles d'être affectés par les modifications apportées depuis la dernière exécution. D'autres IDE / plates-formes / langages / frameworks peuvent avoir des fonctionnalités similaires.
Gardez le commit aussi vite que possible. L'application de règles de style est acceptable, car souvent, c'est le seul moyen de le faire et parce que ces vérifications sont souvent incroyablement rapides. Effectuer une analyse statique est acceptable dès lors que cela reste rapide, ce qui est rarement le cas. Exécuter des tests unitaires n'est pas correct.
Exécutez des tests unitaires sur votre serveur d'intégration continue.
Assurez-vous que les développeurs sont automatiquement informés quand ils ont cassé la construction (ou quand les tests unitaires échouent, ce qui est pratiquement la même chose si vous considérez un compilateur comme un outil qui vérifie certaines des erreurs possibles que vous pouvez introduire dans votre code).
Par exemple, consulter une page Web pour vérifier les dernières versions n'est pas une solution. Ils devraient être informés automatiquement . Afficher une popup ou envoyer un SMS sont deux exemples de la façon dont ils peuvent être informés.
Assurez-vous que les développeurs comprennent que casser la construction (ou échouer les tests de régression) n’est pas correct, et que dès que cela se produit, leur priorité est de la réparer. Peu importe qu'ils travaillent sur une fonction hautement prioritaire que leur supérieur hiérarchique a demandé à envoyer pour demain: ils ont échoué à la construction, ils doivent la réparer.
Sécurité
Le serveur qui héberge le référentiel ne doit pas exécuter de code personnalisé, tel que des tests unitaires, notamment pour des raisons de sécurité. Ces raisons ont déjà été expliquées dans CI Runner sur le même serveur de GitLab?
Si, par contre, votre idée est de lancer un processus sur le serveur de construction à partir du hook de pré-validation, cela ralentira encore plus les validations.
la source
Permettez-moi d'être le seul à être en désaccord avec mes collègues intervenants.
Ceci est connu sous le nom de Gated Check-ins dans le monde TFS, et je m'attends à ce que ce soit ailleurs. Lorsque vous essayez de vous enregistrer dans une branche avec l'enregistrement synchronisé, l'ensemble d'étagères est envoyé au serveur, ce qui garantit que vos modifications sont générées et que les tests unitaires spécifiés (en lecture) réussissent. S'ils ne le font pas, il vous avertit que vous êtes un mauvais singe qui a cassé la construction. S'ils le font, les modifications vont dans le contrôle de source (yay!).
D'après mon expérience, les enregistrements sécurisés sont l'un des processus les plus importants pour le succès des tests unitaires - et par extension de la qualité logicielle.
Pourquoi?
Et bien sûr, il y a l'avantage que vous avez évoqué à l'origine - lorsque vous avez des check-ins sécurisés et une suite solide de tests, chaque ensemble de modifications est "stable". Vous économisez toute la surcharge (et le potentiel d'erreur) de "quand était la dernière bonne construction?" - toutes les constructions sont assez bonnes pour se développer contre.
Oui, il faut du temps pour construire et exécuter les tests. D'après mon expérience, 5 à 10 minutes pour une application C # de bonne taille et des tests unitaires de ~ 5 000 unités. Et ça m'est égal. Oui, les gens devraient s’enregistrer fréquemment. Mais ils doivent également mettre à jour leurs tâches fréquemment, ou consulter leur courrier électronique, ou prendre un café ou des dizaines d'autres choses "ne travaillant pas avec du code" qui constituent le travail d'un ingénieur logiciel pour occuper ce temps. La vérification du mauvais code coûte beaucoup plus cher que 5 à 10 minutes.
la source
Les commits devraient être rapides. Lorsque je commets du code, je souhaite qu’il soit transmis au serveur. Je ne veux pas attendre quelques minutes pendant qu'il exécute une batterie de tests. Je suis responsable de ce que je pousse sur le serveur et je n'ai besoin de personne pour me garder avec des crochets de validation.
Cela dit, une fois qu’il arrive sur le serveur, il doit être analysé, testé et construit immédiatement (ou dans un court délai). Cela m'indiquerait que les tests unitaires sont rompus, que la compilation n'a pas eu lieu ou que j'ai créé un désordre avec les outils d'analyse statique disponibles. Plus cela est fait rapidement (la construction et l’analyse), plus vite mes réactions et mes réponses sont rapides (les pensées n’ont pas complètement disparu de mon cerveau).
Donc non, ne mettez pas de tests et autres dans des hooks de commit sur le client. Si vous devez, mettez-les sur le serveur dans un post-commit (car vous n'avez pas de serveur de CI) ou sur le serveur de build de CI et avertissez-moi correctement des problèmes liés au code. Mais ne bloquez pas le commit en premier lieu.
Je devrais également souligner qu'avec certaines interprétations du développement piloté par les tests, il convient de vérifier un test unitaire qui se déclenche en premier . Ceci démontre et documente le bug est présent. Ensuite, un enregistrement ultérieur serait le code qui corrige le test unitaire. Empêcher toute vérification jusqu'à la réussite des tests unitaires réduirait la valeur effective de la vérification dans un test unitaire qui ne documente pas le problème.
Connexes: Devrais-je avoir des tests unitaires pour les défauts connus? et Quelle est l'utilité de vérifier les tests unitaires échoués?
la source
Commits should run fast.
Quels sont les avantages de cela? Je suis curieux parce que nous utilisons actuellement des checkins gated. Habituellement, mes checkins cumulent environ une heure de travail, donc 5 minutes d’attente ne sont pas un gros problème. En fait, j'ai constaté que c'est normalement lorsque je suis pressé que la compilation de validation est très utile pour détecter les erreurs stupides (suite à une précipitation)catching silly mistakes (as a result of rushing)
. se précipiter en général est une mauvaise pratique en génie logiciel. Robert C. Martin recommande d'écrire du code comme pour une opération youtube.com/watch?v=p0O1VVqRSK0En principe, je pense qu'il est logique d'empêcher les gens d'apporter des modifications à la ligne principale qui briseraient la construction. En d’autres termes, le processus de modification de la branche principale de votre référentiel doit exiger que tous les tests passent toujours. Casser la construction est tout simplement trop coûteux en temps perdu pour que tous les ingénieurs du projet puissent faire autre chose.
Cependant, la solution particulière de commit hooks n'est pas un bon plan.
la source
Breaking the build is simply too costly in terms of lost time for all engineers on the project
Je suggérerais d'utiliser une sorte d'outil de notification de build pour éviter que tous les ingénieurs finissent par perdre du temps sur chaque build casséNon, vous ne devriez pas, comme d'autres réponses l'ont souligné.
Si vous voulez avoir une base de code qui ne présente aucun test en échec, vous pouvez plutôt développer sur des branches de fonctionnalités et extraire les demandes dans le maître. Vous pouvez ensuite définir les conditions préalables pour accepter ces demandes d'extraction. L'avantage est que vous pouvez pousser très vite et que les tests sont exécutés en arrière-plan.
la source
Avoir à attendre une construction réussie et des tests sur chaque engagement dans la branche principale est vraiment horrible, je pense que tout le monde est d'accord sur ce point
Mais il existe d'autres moyens de créer une branche principale cohérente. Voici une suggestion, un peu similaire dans le sens des archivages contrôlés dans TFS, mais qui peut être généralisée à tout système de contrôle de version avec des branches, bien que j'utilise principalement les termes git:
Avoir une branche intermédiaire dans laquelle vous ne pouvez commettre que des fusions entre vos branches dev et la branche principale
Configurez un point d'ancrage qui démarre ou met en file d'attente une construction et teste les validations effectuées sur la branche intermédiaire, mais cela ne fait pas attendre le commetteur
Surchauffe et les tests avec succès, faire automatiquement la branche principale aller de l' avant si elle est à jour
Remarque: ne fusionnez pas automatiquement dans la branche principale, car la fusion testée, sinon une fusion en aval du point de vue de la branche principale, peut échouer si elle est fusionnée dans la branche principale avec des validations intermédiaires.
En conséquence:
Interdire l'homme s'engage dans la branche principale, automatiquement si vous le pouvez, mais également dans le cadre du processus officiel en cas d'échappatoire ou s'il n'est pas techniquement possible de l'appliquer.
Au moins, vous pouvez vous assurer que personne ne le fera de manière involontaire ou sans malice, une fois que ce sera une règle de base. Vous ne devriez pas essayer de le faire, jamais.
Vous devrez choisir entre:
Une seule branche de transfert, qui fera que les fusions autrement réussies échouent si une fusion précédente, mais pas encore créée ni testée, échoue
Au moins, vous saurez quelle fusion a échoué et que quelqu'un la corrige, mais les fusions entre les deux ne sont pas traçables de manière triviale (par le système de contrôle de version) pour les résultats de la construction et des tests ultérieurs.
Vous pouvez consulter les annotations de fichiers (ou blammer), mais parfois, une modification dans un fichier (par exemple, la configuration) génère des erreurs dans des endroits inattendus. Cependant, c'est un événement plutôt rare.
Plusieurs branches de transfert, ce qui permettra aux fusions réussies sans conflit d'accéder à la branche principale
Même dans le cas où une autre branche intermédiaire possède des fusions défaillantes non conflictuelles . La traçabilité est un peu meilleure, moins le cas où une fusion ne s'attendait pas à un changement affectant d'une autre fusion. Mais encore une fois, c’est assez rare pour ne pas s’inquiéter de chaque jour ou de chaque semaine.
Pour que les fusions ne soient pas en conflit la plupart du temps, il est important de séparer les branches de planification de manière judicieuse, par exemple, par équipe, par couche ou par composant / projet / système / solution (quel que soit le nom choisi).
Si la branche principale a été transmise entre-temps à une autre fusion, vous devez fusionner à nouveau. Espérons que ce n’est pas un problème de fusions non conflictuelles ou de très peu de conflits.
Par rapport aux enregistrements synchronisés, l’avantage ici est que vous avez la garantie d’une branche principale opérationnelle, car la branche principale est uniquement autorisée à aller de l’avant, pas à fusionner automatiquement vos modifications avec les modifications effectuées entre celles-ci. Donc, le troisième point est la différence essentielle.
la source
Je préfère que "tests unitaires réussis" soit une porte pour la soumission de code. Cependant, pour que cela fonctionne, vous aurez besoin de quelques choses.
Vous aurez besoin d'un framework de construction qui mette en cache les artefacts.
Vous aurez besoin d'une infrastructure de test qui mette en cache le statut des tests exécutés (avec succès), avec n'importe quel artefact donné.
De cette façon, les archivages avec réussite des tests unitaires seront rapides (vérification croisée de la source sur l'artefact construit lors de la vérification des tests par le développeur), ceux dont les tests unitaires ont échoué seront bloqués et les développeurs qui oubliez pas de vérifier que les compilations avant de commettre seront encouragées à s'en souvenir la prochaine fois, car le cycle de compilation et de test est long.
la source
Je dirais que cela dépend du projet et de la portée des tests automatisés exécutés sur le "commit".
Si les tests que vous souhaitez exécuter dans le déclencheur d’enregistrement sont très rapides, ou si le flux de travail du développeur va forcer un peu d’administration après un tel enregistrement, je pense que cela ne devrait pas avoir beaucoup d’importance et forcer les développeurs à vérifier uniquement dans des choses qui exécute absolument les tests les plus élémentaires. (Je suppose que vous n'exécuteriez que les tests les plus élémentaires sur un tel déclencheur.)
Et je pense que, si la vitesse et le flux de travail le permettent, c’est une bonne chose de ne pas transmettre les modifications aux autres développeurs qui échouent aux tests - et vous savez seulement si elles échouent si vous les exécutez.
Vous écrivez "commettre ... dans une branche distante" dans la question, ce qui impliquerait pour moi que ce n'est (a) pas quelque chose qu'un développeur fait toutes les quelques minutes, donc une petite attente peut être très bien acceptable, et (b) qu'après Une telle validation des modifications de code peut avoir un impact sur d'autres développeurs, de sorte que des vérifications supplémentaires peuvent être nécessaires.
Je suis d’accord avec les autres réponses sur "ne faites pas attendre vos développeurs en attendant" pour une telle opération.
la source
Les commits cassés ne devraient pas être autorisés sur le tronc , car c'est le tronc qui peut entrer en production. Donc, vous devez vous assurer qu'il y a une passerelle à passer avant d'être connecté au tronc . Cependant, les commits cassés peuvent être totalement acceptables dans un repo tant qu'ils ne sont pas sur le tronc.
D'autre part, obliger les développeurs à attendre / résoudre les problèmes avant de transmettre les modifications au référentiel présente un certain nombre d'inconvénients.
Quelques exemples:
la source