web-dev-qa-db-fra.com

Écriture de tests pour le code existant

Supposons que l'on ait un programme relativement important (disons 900k SLOC en C #), tous commentés/documentés à fond, bien organisés et fonctionnant bien. L'intégralité de la base de code a été écrite par un seul développeur senior qui ne fait plus partie de l'entreprise. Tout le code est testable tel quel et l'IoC est utilisé partout - sauf pour une raison étrange, ils n'ont écrit aucun test unitaire. Désormais, votre entreprise souhaite créer une branche du code et souhaite que des tests unitaires soient ajoutés pour détecter les changements qui interrompent la fonctionnalité principale.

  • L'ajout de tests est-il une bonne idée?
  • Si oui, comment pourrait-on même commencer sur quelque chose comme ça?

MODIFIER

OK, donc je ne m'attendais pas à des réponses faisant de bons arguments pour des conclusions opposées. Le problème est peut-être hors de ma portée de toute façon. J'ai également lu les "questions en double" et le consensus général est que "la rédaction des tests est bonne" ... oui, mais pas trop utile dans ce cas particulier.

Je ne pense pas que je suis seul ici à envisager d'écrire des tests pour un système hérité. Je vais garder des mesures sur le temps passé et le nombre de fois que les nouveaux tests détectent des problèmes (et combien de fois ils ne le font pas). Je reviendrai et le mettrai à jour dans un an environ avec mes résultats.

CONCLUSION

Il s'avère donc qu'il est fondamentalement impossible d'ajouter simplement un test unitaire au code existant avec un semblant d'orthodoxie. Une fois que le code fonctionne, vous ne pouvez évidemment pas passer au rouge ou au vert vos tests, il n'est généralement pas clair quels comportements sont importants à tester, pas clair par où commencer et certainement pas clair quand vous avez terminé. Vraiment, même poser cette question passe à côté du point principal de la rédaction des tests. Dans la majorité des cas, j'ai trouvé qu'il était en fait plus facile de réécrire le code à l'aide de TDD que de déchiffrer les fonctions prévues et d'ajouter rétroactivement des tests unitaires. Lors de la résolution d'un problème ou de l'ajout d'une nouvelle fonctionnalité, c'est une autre histoire, et je pense que c'est le moment d'ajouter des tests unitaires (comme certains l'ont souligné ci-dessous). Finalement, la plupart du code est réécrit, souvent plus tôt que prévu - grâce à cette approche, j'ai pu ajouter une couverture de test à une partie étonnamment grande de la base de code existante.

70
Paul

Bien que les tests soient une bonne idée, l'intention était que le codeur d'origine les construise pendant qu'il construisait l'application pour capturer sa connaissance de la façon dont le code est censé fonctionner et ce qui pourrait casser, qui vous aurait alors été transféré.

En adoptant cette approche, il y a une forte probabilité que vous écriviez les tests les moins susceptibles de se casser et que vous ratiez la plupart des cas Edge qui auraient été découverts lors de la création de l'application.

Le problème est que la plus grande partie de la valeur proviendra de ces "pièges" et de situations moins évidentes. Sans ces tests, la suite de tests perd pratiquement toute son efficacité. De plus, l'entreprise aura un faux sentiment de sécurité autour de son application, car elle ne sera pas significativement plus résistante à la régression.

Généralement, la façon de gérer ce type de base de code consiste à écrire des tests pour le nouveau code et pour la refactorisation de l'ancien code jusqu'à ce que la base de code héritée soit entièrement refactorisée.

Aussi voir .

69
FMJaguar

Oui, l'ajout de tests est définitivement une bonne idée.

Vous dites que c'est bien documenté, et cela vous met dans une bonne position. Essayez de créer des tests en utilisant cette documentation comme guide, en vous concentrant sur les parties du système qui sont critiques ou sujettes à des changements fréquents.

Au début, la taille de la base de code semblera probablement écrasante par rapport à la petite quantité de tests, mais il n'y a pas d'approche big-bang, et prendre un départ quelque part est plus important que d'agoniser sur ce que la meilleure approche sera.

Je recommanderais le livre de Michael Feathers, Working Effectively with Legacy Code, pour de bons conseils détaillés.

35
Danny Woods

Tous les tests unitaires n'ont pas les mêmes avantages. L'avantage d'un test unitaire vient quand il échoue. Moins il est susceptible d'échouer, moins il est bénéfique. Le code nouveau ou récemment modifié est plus susceptible de contenir des bogues que le code rarement modifié qui est bien testé en production. Par conséquent, les tests unitaires sur le code nouveau ou récemment modifié sont plus susceptibles d'être plus avantageux.

Tous les tests unitaires n'ont pas le même coût. Il est beaucoup plus facile de tester un code trivial que vous avez conçu vous-même aujourd'hui qu'un code complexe que quelqu'un d'autre a conçu il y a longtemps. De plus, les tests pendant le développement permettent généralement de gagner du temps. Sur le code hérité, les économies de coûts ne sont plus disponibles.

Dans un monde idéal, vous auriez tout le temps dont vous avez besoin pour tester le code hérité unitaire, mais dans le monde réel, à un moment donné, il va de soi que les coûts d'ajout de tests unitaires au code hérité l'emporteront sur les avantages. L'astuce consiste à identifier ce point. Votre contrôle de version peut vous aider en vous montrant le code le plus récemment modifié et le plus fréquemment modifié, et vous pouvez commencer par les mettre sous test unitaire. En outre, lorsque vous apportez des modifications à l'avenir, mettez ces modifications et le code étroitement lié sous test unitaire.

En suivant cette méthode, vous aurez finalement une assez bonne couverture dans les zones les plus bénéfiques. Si vous passez des mois à mettre en place des tests unitaires avant de reprendre des activités génératrices de revenus, cela pourrait être une décision de maintenance logicielle souhaitable, mais c'est une mauvaise décision commerciale.

21
Karl Bielefeldt

L'ajout de tests est-il une bonne idée?

Absolument, bien que je trouve un peu difficile de croire que le code est propre et fonctionne bien et utilise des techniques modernes et n'a tout simplement pas de tests unitaires. Êtes-vous sûr qu'ils ne sont pas assis dans une solution distincte?

Quoi qu'il en soit, si vous allez étendre/maintenir le code, les vrais tests unitaires sont inestimables pour ce processus.

Si oui, comment pourrait-on même commencer sur quelque chose comme ça?

Un pas après l'autre. Si vous n'êtes pas familier avec les tests unitaires, alors apprenez un peu. Une fois que vous êtes à l'aise avec les concepts, choisissez une petite section du code et écrivez des tests pour celui-ci. Puis le suivant et le suivant. La couverture du code peut vous aider à trouver les endroits que vous avez manqués.

Il est probablement préférable de choisir les choses dangereuses/risquées/vitales à tester en premier, mais vous pourriez être plus efficace en testant quelque chose de simple pour entrer dans un groove en premier - surtout si vous/l'équipe n'êtes pas habitué à la base de code et/ou à l'unité essai.

9
Telastyn

Oui, avoir des tests est une bonne idée. Ils aideront à documenter les travaux de base de code existants comme prévu et à détecter tout comportement inattendu. Même si les tests échouent initialement, laissez-les, puis refactorisez le code plus tard afin qu'ils passent et se comportent comme prévu.

Commencez à écrire des tests pour des classes plus petites (celles qui n'ont pas de dépendances et sont relativement simples) et passez à des classes plus grandes (celles qui ont des dépendances et sont plus complexes). Cela prendra beaucoup de temps, mais soyez patient et persistant afin de pouvoir éventuellement couvrir la base de code autant que possible.

3
Bernard

OK, je vais donner l'avis contraire ....

L'ajout de tests à un système existant et fonctionnel va modifier ce système, à moins que ce ne soit le cas, tout le système est écrit en se moquant du début. J'en doute, bien qu'il soit tout à fait possible qu'il ait une bonne séparation de tous les composants avec des limites facilement définissables dans lesquelles vous pouvez glisser vos interfaces fictives. Mais si ce n'est pas le cas, vous devrez faire des changements assez importants (relativement parlant) qui pourraient bien casser les choses. Dans le meilleur des cas, vous allez passer beaucoup de temps à écrire ces tests, du temps qui pourrait être mieux dépensé à écrire une charge de documents de conception détaillés, de documents d'analyse d'impact ou de documents de configuration de solution. Après tout, c'est le travail que votre patron veut faire plus que des tests unitaires. N'est-ce pas?

Quoi qu'il en soit, je n'ajouterais aucun test unitaire.

Je me concentrerais sur des outils de test externes et automatisés qui vous donneront une couverture raisonnable sans rien changer. Ensuite, lorsque vous venez d'apporter des modifications ... c'est là que vous pouvez commencer à ajouter des tests unitaires à l'intérieur de la base de code.

3
gbjbaanb

J'ai souvent rencontré cette situation, hérité d'une grande base de code sans couverture de test adéquate ou inexistante, et maintenant je suis responsable de l'ajout de fonctionnalités, de la correction des bogues, etc.

Mon conseil est de vous assurer et de tester ce que vous ajoutez, et si vous corrigez des bogues ou modifiez des cas d'utilisation dans le code actuel, créez des tests ensuite. Si vous devez toucher quelque chose, écrivez des tests à ce stade.

Lorsque cela tombe en panne, c'est lorsque le code existant n'est pas bien structuré pour les tests unitaires, vous passez donc beaucoup de temps à refactoriser afin de pouvoir ajouter des tests pour des modifications mineures.

2
Casey