Meilleures pratiques pour moderniser le code hérité avec des tests automatisés

22

Je suis sur le point d'assumer la tâche de réimplémenter une interface déjà définie (un ensemble de fichiers d'en-tête C ++) dans une base de code relativement grande et ancienne. Avant de faire cela, je voudrais avoir une couverture de test aussi complète que possible, afin que je puisse détecter les erreurs de réimplémentation le plus tôt et le plus facilement possible. Le problème est que la base de code déjà existante n'a pas été conçue pour être facilement testable, avec des classes et des fonctions (très) grandes, un haut degré de couplage, des fonctions avec (de nombreux) effets secondaires, etc.

Ce serait bien d'entendre parler de toute expérience antérieure avec des tâches similaires, et de bons conseils concrets sur la façon dont vous avez procédé à la conversion des tests automatisés (unité, intégrations, régression, etc.) à votre code hérité.

tjansson
la source
1
Étape 1: recherche de dépassement de pile. La question a été posée. Beaucoup, plusieurs fois.
S.Lott

Réponses:

20

Tout d'abord, obtenez et lisez Travailler efficacement avec le code hérité de Michael Feathers - c'est une aide indispensable pour de telles tâches.

Ensuite, quelques notes:

  • avez-vous une spécification / contrat précis pour l'interface, ou avez-vous pratiquement uniquement l'implémentation existante en tant que "spécification"? Dans le premier cas, il est plus facile de faire une réécriture complète à partir de zéro, dans le second, il est difficile, voire impossible.
  • si vous souhaitez réimplémenter l'interface, le moyen le plus utile de dépenser vos ressources de test est d'écrire des tests uniquement sur l'interface. Bien sûr, cela ne constitue pas un test unitaire au sens strict, plutôt un test fonctionnel / d'acceptation, mais je ne suis pas un puriste :-) Cependant, ces tests sont réutilisables et vous permettent de comparer directement les résultats des deux implémentations côte à côte .
  • dans l'ensemble, je préférerais refactoriser le code existant plutôt que de réécrire à partir de zéro, à moins qu'il ne soit complètement impossible à maintenir. (Mais dans ce cas, comment allez-vous de toute façon écrire des tests unitaires?) Consultez ce post de Joel pour une discussion plus détaillée sur le sujet. La création d'un ensemble de tests d'acceptation contre l'interface vous donne un filet de sécurité mince mais utile, contre lequel vous pouvez commencer à refactoriser avec précaution le code existant pour le rendre testable unitaire (en utilisant les idées du livre de Feathers).
Péter Török
la source
Je voudrais +3 si je le pouvais. WELC est une lecture essentielle et optez définitivement pour une refactorisation ...
Johnsyweb
Un petit commentaire sur le deuxième point est que pour les systèmes hérités, les tests doivent être effectués selon l' état d'esprit du test de caractérisation . Autrement dit, capturez fidèlement le comportement actuel du logiciel et évitez de modifier le comportement même si certains des résultats des tests semblent étranges ou non agréables selon l'état d'esprit des tests unitaires. (Btw cette idée vient également de l'auteur de WELC.)
rwong
@rwong, en effet. Sans spécification détaillée ou propriétaire de produit compétent, il est impossible pour le développeur de décider si un comportement spécifique du programme a) est intentionnel et requis, b) était involontaire mais à ce jour les utilisateurs en dépendent, c) un bug qui nuit réellement aux utilisateurs, d) un bug totalement inaperçu jusqu'à présent. Dans les deux premiers cas, "réparer" cela nuirait réellement aux utilisateurs, et dans le dernier cas, la correction - bien que théoriquement correcte - n'apporterait aucun avantage visible.
Péter Török
4

La meilleure méthode est la méthode Mikado. http://mikadomethod.wordpress.com/2010/08/04/the-mikado-method-book/ Ce n'est que la généralisation d'une technique simple mais c'est la seule façon que je connaisse pour commencer à améliorer la qualité du code dans une grande base de code sans prendre de risques inutiles.

WEWLC est également un très bon livre à ce sujet mais être écrit en C ++ n'est pas toujours utile avec du code Java ou Ruby.

Uberto
la source
2

Les tests d'ajustement rétroactif sur une ancienne base de code peuvent être assez difficiles si leur conception est monolithique.

Si possible (avez-vous le temps / l'argent), une façon d'avancer serait de refactoriser le code en unités plus testables.

ozz
la source
1

Je voudrais ajouter un lien . Il y a peu d'exemples d'implémentations testables pas si facilement remodelées dans un code plus convivial pour xUnit. En ce qui concerne l'approche générale, essayez les liens déjà mentionnés (publication de Joel, code Working With Legacy

yoosiba
la source