Y a-t-il une valeur à écrire des tests unitaires pour du code qui fonctionne déjà lors de l'héritage d'applications?

27

De toute évidence, certaines anciennes applications ne peuvent pas être ou sont extrêmement difficiles à tester en raison de la façon dont elles ont été écrites en premier lieu.

Mais dans certains endroits, comme certaines méthodes d'assistance qui pourraient probablement être testées unitairement, devrais-je prendre la peine d'écrire des tests unitaires pour elles?

Je veux dire, ils pourraient être écrits comme un chien, mais quelle que soit la complexité de la logique, l'épreuve du temps a prouvé que cela fonctionne comme il se doit. Dois-je prendre la peine de faire des tests unitaires uniquement si je dis le refactoriser ou dois-je leur écrire des tests unitaires chaque fois que j'ai le temps?

Y a-t-il une valeur à faire cela?

Tenez également compte du fait que la définition de la méthode peut être vague, et je devrai peut-être rechercher ce que certaines méthodes devraient réellement faire dans des conditions données.

RoboShop
la source
4
Rendez-vous service et lisez Travailler efficacement avec le code hérité . C'est un livre entier consacré à répondre à cette question et à d'autres questions connexes.
Rein Henrichs

Réponses:

15

Je pense que vous devriez certainement écrire autant de tests que possible pour l'application. Ils vous aideront à apprendre la base de code et vous prépareront à la refactorisation éventuelle ou au nouveau développement.

Il existe quelques types de tests que vous pouvez écrire dans ce scénario, chacun d'eux a ses propres mérites. L'écriture de ces tests vous en apprendra beaucoup sur l'application que vous utilisez.

Tout d'abord, avant de commencer à écrire des tests d'exactitude, écrivez des tests qui capturent le comportement actuel , qu'il soit bon ou mauvais. Il y a fort à parier que vous allez découvrir des bogues dans des cas d'angle ou dans des parties du code qui n'ont pas été minutieusement testées en exécutant le programme. Ne vous inquiétez pas de ce que le code devrait faire, capturez simplement ce qu'il fait. Au fur et à mesure que vous progressez, ne vous inquiétez pas de lire le code ou de passer du temps à déterminer ce que devrait être la sortie. Exécutez simplement votre test et capturez cette sortie dans une assertion.

Cela vous donnera une base solide de compréhension du fonctionnement du code et de l'emplacement des principaux points douloureux ou des points faibles. Si vous découvrez des bogues, vous pouvez alors approcher les personnes ayant le pouvoir de décider si elles valent la peine d'être corrigées et de prendre ces décisions.

Ensuite, vous pouvez écrire quelques tests plus volumineux (dans la portée) qui couvrent des parties du code qui peuvent ne pas être facilement testables par unité, mais où il serait toujours important de tester les flux de travail autant que possible. Ces tests de flux de travail ou tests d' intégration , selon la façon dont vous les regardez, vous fourniront une bonne base pour refactoriser ces flux de travail afin de les rendre plus testables et de vous protéger lorsqu'une nouvelle fonctionnalité doit être ajoutée qui pourrait affecter un flux de travail existant.

Au fil du temps, vous allez créer une suite de tests qui sont là pour vous aider ou aider la personne suivante qui finira par hériter de l'application.

Adam Lear
la source
13

Je vois les tests unitaires comme des spécifications, pas comme de simples tests. Dans le cas spécifique que vous avez mentionné, la valeur réelle viendra lorsque vous aurez mis en place un test unitaire avant d'apporter toute modification pour se conformer aux nouvelles spécifications. Si vous allez changer le code hérité, la meilleure façon que je vois est d'avoir des tests unitaires pour la fonctionnalité, cela apportera non seulement un filet de sécurité mais nous aidera également à clarifier ce que fait cette unité particulière. Cela peut également vous obliger à faire des recherches, comme vous l'avez mentionné, ce qui est bon pour la compréhension. Donc, je pense que le test unitaire d'un module est en place avant qu'il ne soit sur le point de subir un changement.

Vinod R
la source
Écrivez les tests dans la langue de niveau le plus élevé qui fonctionnera. Lorsqu'on lui a demandé d'estimer le temps nécessaire pour effectuer un changement, indiquez le temps nécessaire pour écrire les tests manquants. La modification peut prendre plus de temps, mais vous mettrez beaucoup moins de bogues sur le terrain.
kevin cline
8
 > Is there any value in writing unit tests for code that 
 > already works when inheriting applications?

Non . D'après mon expérience, il existe une mauvaise relation coûts / avantages pour l'écriture de tests unitaires après l' écriture du code de production, car il est très peu probable / difficile de trouver des moyens de tester le code isolément sans refactorisation lourde. L'un des avantages du développement piloté par les tests est que vous obtenez du code faiblement couplé. Si vous écrivez les tests par la suite, les chances sont élevées que le code soit étroitement couplé.

OUI, vous pouvez écrire des tests d'intégration pour l'application pour les fonctions pour lesquelles vous prévoyez d'apporter des modifications. De cette façon, vous pouvez avoir un test de régression pour voir si vos modifications cassent quelque chose.

OUI du point de vue de la qualité et du développeur, il est utile d'avoir des tests.

NON du point de vue commercial, car il est très difficile de vendre au client que vous avez besoin de temps / d'argent pour ne pas avoir de valeur commerciale réelle, mais la vague promesse que les changements commerciaux n'aggraveront pas les choses.

k3b
la source
+1 pour les tests d'intégration plutôt que le test unitaire dans ce cas. Conseil très pragmatique
gbjbaanb
5

Si possible, il y a encore plus de raisons d'écrire des tests pour le code existant qui "fonctionne déjà". S'il n'y a pas de tests, il y a de fortes chances que le code soit plein d'odeurs et puisse utiliser une refactorisation approfondie pour être amélioré; la suite de tests vous aidera à refactoriser. Cela pourrait même révéler que le code d'origine ne fonctionne pas correctement; Je me souviens une fois que j'ai écrit un test pour une méthode générant un rapport de vente et que je trouvais que cela ne calculait pas les choses correctement, donc les ventes étaient quelques milliers de dollars de plus qu'elles ne l'étaient vraiment, et personne n'avait su depuis cinq ans que le code était en production (heureusement, ce n'était pas la fin financière réelle des choses, juste une estimation du rapport des ventes).

Bien sûr, ce conseil ne s'applique que si vous pouvez réellement passer les tests. D'après mon expérience, une base de code sans tests est presque impossible de moderniser les tests car vous devrez réécrire la moitié des modules de code pour être suffisamment abstraits pour prendre en charge les tests en premier, et vous ne pourrez pas le faire.

Wayne Molina
la source
Pourquoi un downvote pour avoir dit la vérité?
Wayne Molina
4

Absolument oui , car les tests unitaires ne doivent pas vérifier l'exactitude mais caractériser le comportement réel du système. Surtout avec un projet hérité, de nombreuses exigences sont implicitement encodées dans le comportement réel.

Cette caractérisation sert de base de fonctionnalité. Même si le comportement est incorrect d'un point de vue commercial, vous empêchez le comportement de se dégrader davantage. Il peut être difficile de gagner du terrain dans un projet hérité, et cela vous donne une base de référence afin que vous puissiez avancer sans aggraver les choses.

Alors, écrivez tous les tests unitaires que vous pouvez. Pour le code qui ne peut pas être testé, refactorisez votre code pour séparer les dépendances en introduisant des "coutures" dans le code - des emplacements où vous pouvez séparer le code que vous voulez tester du code que vous ne voulez pas tester.

Les idées de tests unitaires en tant que tests de caractérisation et coutures sont les points principaux de Working Effectively with Legacy Code de Michael Feathers, un livre que je recommande fortement.

Matthew Rodatus
la source
2

J'ai réfléchi à ce sujet moi-même car il y a beaucoup de documents non documentés mal écrits, mais fonctionnant dans notre système.

Vous faites une remarque fine:

Dois-je prendre la peine de faire des tests unitaires uniquement si je dis le refactoriser ou dois-je leur écrire des tests unitaires chaque fois que j'ai le temps?

Si les exigences changent, je pense qu'il serait très utile d'écrire les tests unitaires. Sinon, cela ne me dérangerait pas d'autant plus que vous ne seriez pas en mesure de passer des tests appropriés, car vous ne connaissez pas toutes les conditions.

chaque fois que j'ai le temps?

Vous avez le temps quand il est priorisé, non?, Donc ça n'arrivera jamais de toute façon. :)

Michael
la source
1

Essayez-vous d'exploiter des aspects de cette base de code qui n'ont pas été utilisés auparavant dans une application de production? Écrivez d'abord des tests unitaires pour ces scénarios. Vous devez être en mesure de hiérarchiser votre travail ici, et je pense que The Art of Unit Testing a eu quelques conseils à cet égard (en particulier comment aborder les tests des bases de code héritées).

Justin Simon
la source
1

Ne présumez pas que tout fonctionne simplement parce que l'application est opérationnelle ... généralement, le "test du temps" montre seulement que a) vous n'avez pas encore trouvé le reste des défauts ou b) les personnes qui ont trouvé ils ne les ont pas signalés. (Ou peut-être les deux.) Et même le code qui fonctionne probablement maintenant ne fonctionnera peut-être pas la prochaine fois que vous devrez apporter une modification.

Les tests unitaires vous donnent une certaine certitude que certaines fonctionnalités restent intactes, tout en vous fournissant un moyen de le démontrer pour une quantité raisonnable de cas. C'est beaucoup plus précieux que «fouiner et tester cela», même pour une seule correction de bogue. (Fouiller ressemble plus à l'équivalent de tests d'intégration ou de système, donc en ce sens, tester manuellement l'application peut couvrir plus que les tests unitaires seuls, mais c'est encore du temps passé de manière inefficace. Même une certaine méthode d'automatisation de ces tests est mieux que de les faire manuellement.)

Vous devez absolument écrire des tests lorsque vous changez de code, qu'il s'agisse de correction ou de refactoring. Vous feriez bien d'écrire des tests comme vous le pouvez, si pour aucune autre raison que de gagner du temps la prochaine fois que vous devrez corriger un défaut (et il y en aura une prochaine fois) ... bien que dans ce cas, vous doivent équilibrer ce temps par rapport aux attentes des personnes qui prétendent que les logiciels sont toujours exempts de défauts et donc le temps consacré à la maintenance future est «gaspillé». (Cela ne signifie pas pour autant que vous devez ignorer les plannings actuels.) À un moment donné, si vous le faites, vous aurez suffisamment de couverture pour commencer à démontrer la valeur des tests sur les applications héritées, et cela pourrait vous aider à dépenser ce temps sur les futures applications, ce qui devrait éventuellement libérer du temps à consacrer au développement productif.

Dave DuPlantis
la source
0

De mon cœur de pays lala, je dirai oui et non seulement cela, mais piratez-le, refactorisez-le et continuez à tester, mais assurez-vous de ne pas prendre de retard dans d'autres projets importants. En tant que programmeur en activité, si votre travail est à risque ou si un gestionnaire vous a dit de vous en approprier, alors oui. Si l'équipe ou l'entreprise peuvent souffrir, négociez avec votre responsable pour lui faire savoir qu'il est nécessaire de faire des tests pour éviter que l'entreprise ne tombe en panne. S'il dit ne vous inquiétez pas, alors vous êtes hors du crochet (pas nécessairement, la délégation de blâme est une compétence de gestion, alors assurez-vous que vous êtes hors du crochet ... vraiment!)

Bonne chance!

Armando
la source