Devrions-nous toujours tester les bogues unitaires lors de leur correction?

29

Lors de la correction de bogues, il est recommandé, lorsque je travaille, d'écrire d'abord un test qui échoue avec le bogue donné, puis de corriger le code jusqu'à ce que le test réussisse. Cela suit les pratiques TDD et est censé être une bonne pratique, mais j'ai remarqué que cela tend à produire des tests cryptiques qui se rapprochent vraiment de l'implémentation.

Par exemple, nous avons eu un problème lorsqu'un travail a été envoyé, a atteint un certain état, a été abandonné et a recommencé. Pour reproduire ce bogue, un test massif a été écrit avec la synchronisation des threads, beaucoup de moqueries et d'autres choses ... Il a fait le travail, mais maintenant que je refactorise le code, je trouve très tentant de supprimer ce mammouth, car cela nécessiterait vraiment (encore) beaucoup de travail pour s'adapter au nouveau design. Et il ne fait que tester une petite fonctionnalité dans un seul cas spécifique.

D'où ma question: comment tester les bugs difficiles à reproduire? Comment évitez-vous de créer des éléments qui testent l'implémentation et nuisent à la refactorisation et à la lisibilité?

Zonko
la source
Ce cas d'erreur est-il pertinent pour la nouvelle conception? Pour moi, une nouvelle conception consisterait à augmenter la fiabilité de la conception, vous pourriez donc être en mesure de l'utiliser pour justifier la suppression de ce cas de test si ce type de bogue était lié à une erreur dans la conception d'origine.
Thymine
le refactoriseur concerne autre chose, et il est toujours possible dans le nouveau design de modifier légèrement le code et de réintroduire le bogue, ce que surveille le test. J'imagine qu'une alternative au test serait un commentaire dans le code disant "ne te fous pas de ça", mais cela semble être la mauvaise chose à faire: p
Zonko
1
s'il est trop complexe pour un plus simple, faites-le partie du test d'intégration
ratchet freak
Cela ressemble à ceci a besoin d'un test d'intégration et non d'un test unitaire. Basé sur le fait que vous vous moquiez tellement. Une règle générale que j'ai vue est que vous ne testez pas le code qui parle aux composants en dehors de votre base de code (parler à une base de données, lire à partir du système de fichiers, etc.) qui ressemble à ce que cela fait également.
Lucas

Réponses:

27

Oui, en général, vous devriez . Comme pour toutes les directives, vous devrez utiliser votre meilleur jugement lorsqu'elles vont à l'encontre d'autres directives. Pour ce scénario, la gravité du bogue doit être mise en balance avec le travail nécessaire pour implémenter le test et la qualité de ce test en ciblant le problème métier et en rattrapant la régression de l'état du bogue.

J'aurais tendance à préférer l'écriture de tests à non, car les interruptions pour éliminer les bogues ont tendance à avoir plus de frais généraux que de simplement développer et maintenir un test unitaire.

Telastyn
la source
J'ajouterais plus à cela et déclarerais que dans un monde idéal, il ne serait considéré comme un bug que si un test unitaire échouait, mais +1 pour l'italique et notant que les besoins des entreprises devaient prévaloir.
Joshua Drake
2
eh bien, en fin de compte, il s'agit d'un équilibre entre le temps nécessaire pour maintenir le test et le temps qu'il faudrait à un noob pour détecter et corriger le bogue s'il se reproduisait.
Zonko
Je préférerais également généraliser le test de sorte qu'il ne s'agit pas seulement de tester l'interruption et la nouvelle tentative du travail lorsqu'il atteint un état spécifique, mais plutôt de tester l'interruption et la nouvelle tentative à chaque état dans lequel un travail pourrait être.
Old Pro
+1 pour "car les interruptions pour éliminer les bogues ont tendance à avoir plus de frais généraux que de simplement développer et maintenir un test unitaire."
Peter K.
16

Je pense que la meilleure pratique - celle que je suis gêné d'admettre que je ne suis pas souvent - est de créer un système ou un test d'intégration qui démontre le problème observé en production, puis de rechercher les unités responsables du problème, puis rédigez des tests unitaires pour les unités qui démontrent le problème au niveau de l'unité . Une fois que vous avez les tests unitaires, réparez les unités et exécutez tous les tests. À ce stade, il peut être prudent de rejeter le test d'origine - car il peut être fragile et / ou lent - mais conservez les tests unitaires dans votre suite automatisée pour des raisons de régression et de couverture.

Carl Manaster
la source
7

La pratique de l'écriture d'un test pour identifier le défaut est une bonne idée, car elle vous permet d'identifier exactement les étapes nécessaires pour reproduire le défaut et vérifier qu'il a été corrigé. De plus, ces tests peuvent être exécutés dans le cadre de tests de fumée ou de tests de régression pour s'assurer que les modifications ultérieures n'ont pas réintroduit un ancien défaut dans le système.

La première chose à considérer est le niveau de test requis. Peut-être que le test pour vérifier le correctif serait plus approprié au niveau des systèmes, ou peut-être même un test d'acceptation effectué manuellement. Je pense qu'il est plus important d'avoir un test documenté et géré, quelle que soit la façon dont il est spécifiquement mis en œuvre.

En ce qui concerne la manière dont le refactoring affecte les tests, cela dépend des caractéristiques spécifiques. Dans certains cas, une refactorisation (ou tout type de travail, comme de nouvelles fonctionnalités) pourrait rendre les tests inutiles. Le problème, tel qu'il s'est produit à l'origine, pourrait ne plus être possible. Dans ce cas, il peut être judicieux de supprimer le test des tests possibles pour rendre votre processus de test (automatisé ou manuel) plus léger. Dans d'autres cas, il existe plusieurs méthodes pour effectuer le test et la vérification de la fonctionnalité à un niveau différent peut être plus appropriée. Si la fonctionnalité est mineure, le test n'est peut-être plus nécessaire du tout.

Vous pouvez également envisager non seulement de vous fier aux tests, mais également à la journalisation. Par exemple, capturer des informations au moment de l'exécution (avec des niveaux de verbosité variables selon l'environnement - plus verbeux pendant les tests, moins verbeux pendant le déploiement), profiler l'application, capturer les vidages de l'état actuel du système. Si vous pouvez trouver des déclencheurs communs au problème, vous pouvez l'utiliser pour guider les tests à tous les niveaux.

Thomas Owens
la source
5

Oui tu devrais.

Écrire des tests unitaires pour la base de code existante. Lors de la correction d'un bogue, vous devez vous assurer que votre test unitaire échoue - cela vous donnera l'assurance que vous travaillez effectivement sur un bogue. Vous devez ensuite re-factoriser et faire passer le test en corrigeant le bogue.

Ce n'est pas une pratique TDD cependant. Dans les tests TDD, vous pilotez votre conception, mais dans votre cas, la conception a déjà été décidée.

CodeART
la source