Ajout de tests unitaires au code hérité

Avez-vous déjà ajouté des tests unitaires, après coup, au code hérité? À quel point le code était-il compliqué et à quel point il était difficile de se moquer de tout? Le résultat final était-il intéressant?

La meilleure façon, j’ai trouvé, est d’append progressivement les tests unitaires, de ne pas intervenir et de dire que nous allons désormais tester l’unité de l’application.

Donc, si vous voulez toucher le code, trouver des corrections de bogues ou effectuer un refactoring, écrivez d’abord les tests unitaires. Pour les bugs, les tests unitaires vous aideront à prouver où est le problème, car vous pouvez le dupliquer.

En cas de refactoring, vous voudrez écrire des tests unitaires, mais vous constaterez peut-être que le test est impossible à écrire, vous devrez peut-être trouver un niveau élevé, qui appelle la fonction qui sera refactorisée et le test unitaire. Ensuite, pendant que vous réorganisez la fonction offensive, écrivez vos tests pour vous assurer qu’elle fonctionne correctement.

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 base de code héritée (C / C ++)?

Le livre de Michael Feathers “Working efficacement avec 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 testé. Ce que j’ai tiré le plus du livre, ce sont deux modèles nommés “Fonctions de Sprout” et “Classes de Sprout”. Une fonction de sprout est celle qui encapsule les modifications 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.

Oui, et c’est généralement douloureux. J’ai souvent dû écrire des tests d’intégration à la place.

Le livre The Art of Unit Testing a de bons conseils à ce sujet. Il recommande également le livre Travailler efficacement avec Legacy Code ; Je n’ai pas encore lu ce dernier, mais c’est sur ma stack.

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

EDIT: J’ai lu Working Effectively avec Legacy Code, et c’est excellent.

Les tests de caractérisation constituent une alternative aux tests unitaires, également introduits dans Travailler efficacement avec le code existant. J’ai eu des résultats intéressants avec de tels tests. Ils sont plus faciles à configurer que les tests unitaires lorsque vous effectuez des tests à partir de points qui peuvent être testés (appelés couture). L’inconvénient est que lorsqu’un test échoue, vous avez moins d’indications sur l’emplacement du problème car la zone testée peut être beaucoup plus grande qu’avec les tests unitaires. La journalisation aide ici.


Un framework de test unitaire tel que ceux de la famille xUnit peut être utilisé pour écrire des tests de caractérisation.

Dans ces 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) le comportement actuel du code.

Le processus est similaire à celui de TDD:

  • écrire un test pour une partie du code
  • l’exécuter – échouer
  • corriger le test à partir du comportement observé du code
  • l’exécute – passe
  • répéter

Les tests échoueront si vous modifiez le comportement externe du code. Comportement externe du code? sonne familier ? Oui c’est ça, nous y sums. Maintenant, vous pouvez refactoriser le code.

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

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

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

Vous avez souvent un énorme projet de code hérité où vous n’avez aucun test, mais vous devez changer de code pour implémenter une nouvelle fonctionnalité ou refactor. La chose intéressante à propos du code hérité est – ça marche! 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 n’importe quelle sortie possible) et les approuver, car vous savez, cela fonctionne! Une fois que vous avez terminé un tel test et approuvé le résultat, vous êtes vraiment plus en sécurité avec un refactoring, puisque maintenant vous avez “verrouillé” tous les comportements existants.

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

Pour plus d’informations, voir

  • README détaillé dans le github repo des projets
  • discussion sur les nouvelles des pirates
  • discussion sur reddit.com/r/php
  • discussion sur reddit.com/r/programming

Si vous envisagez de refactoriser le code hérité, la création de ces tests unitaires est indispensable. Ne vous inquiétez pas de la moquerie ou du stubing – craignez de tester les entrées et les sorties du système pour que vos modifications ou vos efforts de refactoring ne rompent pas les fonctionnalités actuelles.

Je ne vous mentirai pas, il est difficile d’adapter les tests unitaires au code hérité, mais cela en vaut la peine.

Jetez un coup d’œil à la bibliothèque gratuite d’utilitaires de test d’unité 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 nouveau code et du nouveau code.

Il y a quelque temps, j’ai parlé de l’idée d’une pyramide de tests inversés dans le code hérité de 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 des tests d’acceptation de haut niveau lors de l’utilisation de code hérité. Et puis lentement, étape par étape, en 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 la retraite du code hérité http://www.jbrains.ca/legacy-code-retreat et rechercher cette réunion dans votre région.