web-dev-qa-db-fra.com

Ajout de tests unitaires au code hérité

Avez-vous déjà ajouté des tests unitaires, après coup, au code hérité? Dans quelle mesure le code était-il compliqué et combien il était difficile de tout coller et de se moquer de tout? Le résultat final en valait-il la peine?

76

La meilleure façon, j'ai trouvé, est d'ajouter progressivement les tests unitaires, pas simplement de sauter et de dire que nous allons maintenant tester unitairement l'application.

Donc, si vous allez toucher le code, pour des corrections de bugs ou une refactorisation, écrivez d'abord les tests unitaires. Pour les bogues, les tests unitaires aideront à prouver où se trouve le problème, car vous pouvez le dupliquer.

Si vous effectuez une refactorisation, vous voudrez peut-être écrire des tests unitaires, mais vous pouvez constater que le test est impossible à écrire, vous devrez donc peut-être trouver un niveau élevé, qui appelle la fonction qui sera refactorisée et tester cette partie. Ensuite, pendant que vous refactorisez la fonction offensive, écrivez vos tests afin de vous assurer qu'elle fonctionne comme il se doit.

Il n'y a pas de moyen facile de le faire.

Cette question peut aider avec plus de suggestions. Comment introduisez-vous les tests unitaires dans une grande base de code héritée (C/C++)?

54
James Black

Le livre de Michael Feathers "Working Effectively with Legacy Code" est un livre entier couvrant ce sujet. Michael déclare qu'il est souvent trop difficile d'introduire des tests pour le code hérité car il n'est pas structuré pour être testable. Ce que je retiens le plus du livre, c'est quelques modèles nommés "Fonctions Sprout" et "Classes Sprout". Une fonction de germination est une fonction qui encapsule la modification que vous devez apporter au code. Vous testez ensuite ces fonctions uniquement. La classe sprout est la même idée, sauf que la nouvelle fonctionnalité est contenue dans une classe.

38
Phillip Ngan

Oui, et c'est généralement douloureux. J'ai souvent fini par devoir écrire des tests d'intégration à la place.

Le livre The Art of Unit Testing a quelques bons conseils à ce sujet. Il recommande également le livre Working Effectively with Legacy Code ; Je n'ai pas encore lu ce dernier, mais il est sur ma pile.

EDIT: Mais oui, même une couverture de code minimale en valait la peine. Cela m'a donné confiance et un filet de sécurité pour refactoriser le code.

EDIT: J'ai lu Travailler efficacement avec Legacy Code, et c'est excellent.

8
TrueWill

Regardez également la nouvelle approche dans le domaine des tests unitaires de code hérités - projet Asis , il s'inspire du projet ApprovalTests et partage ses concepts clés.

Comme mentionné à propos de l'approche ApprovalTests dans cet article :

Souvent, vous avez un énorme projet de code hérité où vous n'avez aucun test du tout, mais vous devez changer le code pour implémenter une nouvelle fonctionnalité ou refactoriser. La chose intéressante à propos du code hérité est - Cela fonctionne! Cela fonctionne pendant des années, peu importe comment il est écrit. Et c'est un très grand avantage de ce code. Avec les approbations, avec un seul test, vous pouvez obtenir toutes les sorties possibles (HTML, XML, JSON, SQL ou toute autre sortie possible) et approuver, car vous savez - cela fonctionne! Après avoir terminé un tel test et approuvé le résultat, vous êtes vraiment beaucoup plus en sécurité avec une refactorisation, car maintenant vous avez "verrouillé" tous les comportements existants.

L'outil Asis consiste exactement à conserver le code hérité en créant et en exécutant automatiquement des tests de caractérisation.

Pour plus d'informations, consultez

6
zavg

tests de caractérisation est une alternative aux tests unitaires, également présentée dans Utilisation efficace du code hérité. J'ai eu des résultats intéressants avec de tels tests. Ils sont plus faciles à configurer que les tests unitaires car vous testez à partir d'un point que vous pouvez tester (appelé couture). L'inconvénient est que lorsqu'un test échoue, vous avez moins d'indication sur l'emplacement du problème car la zone sous test peut être beaucoup plus grande qu'avec les tests unitaires. La journalisation aide ici.


Un framework de tests unitaires comme ceux de la famille xUnit peut être utilisé pour écrire des tests de caractérisation.

Dans de tels tests, écrits après les faits, les assertions vérifient le comportement actuel du code. Contrairement aux tests unitaires, ils ne prouvent pas que le code est correct, ils épinglent (caractérisent) simplement le comportement actuel du code.

Le processus est similaire à celui du TDD:

  • écrire un test pour une partie du code
  • l'exécuter - échouer
  • corriger le test à partir du comportement observé du code
  • l'exécuter - passer
  • répéter

Les tests échoueront si vous modifiez le comportement externe du code. Comportement externe du code? sonne familier ? Oui, nous y sommes. Vous pouvez maintenant refactoriser le code.

Évidemment, le risque dépend de la couverture des tests de caractérisation.

5
philant

Jetez un œil à la bibliothèque d'utilitaires de test unitaire libre et open source, ApprovalTests . Si vous êtes un développeur .NET, le créateur, Llewellyn Falco, a réalisé une série de vidéos montrant comment il utilise ApprovalTests pour améliorer les tests unitaires du code nouveau et hérité.

5
Lynn Langit

Si vous prévoyez de refactoriser le code hérité, la création de ces tests unitaires est un must. Ne vous inquiétez pas des moqueries ou du stubbing - craignez de tester les entrées et sorties du système afin que vos modifications ou vos efforts de refactoring ne cassent pas les fonctionnalités actuelles.

Je ne vous mentirai pas, l'adaptation des tests unitaires au code hérité est difficile - mais cela en vaut la peine.

4
Andrew Hare

J'ai parlé il y a quelque temps de l'idée d'une pyramide de tests inversée dans le code hérité sur XPDays http://xpdays.com.ua/archive/xp-days-ukraine-2012/materials/legacy-code/

Cette présentation devrait répondre à la question de savoir pourquoi il est parfois si important de commencer par des tests d'intégration/fonctionnels ou même de haut niveau lors de l'utilisation de code hérité. Et puis lentement, étape par étape, introduisant des tests unitaires. Il n'y a pas d'exemples de code - désolé, mais vous pouvez en trouver des tas dans le livre de Michaels Feathers "Travailler efficacement avec Legacy Code".

Vous pouvez également consulter Legacy Code Retreat http://www.jbrains.ca/legacy-code-retreat et rechercher cette réunion dans votre région.

1
streser