web-dev-qa-db-fra.com

Comment écrire des tests unitaires pour le code hérité (que je ne comprends pas)?

Vers l'avant

J'ai lu beaucoup de choses avant de poser cette question, y compris de nombreuses questions pertinentes ici sur SE:

Cependant, je ne peux pas m'empêcher de penser que la démangeaison n'a pas encore été rayée après avoir lu pour obtenir de l'aide.


TL; DR

Comment écrire des tests unitaires pour du code hérité que je ne peux pas exécuter, simuler, lire ou comprendre facilement? Quels tests de régression sont utiles à un composant qui fonctionne vraisemblablement comme prévu?


L'image entière

Je suis à nouveau stagiaire d'été alors que je fais la transition vers les études supérieures. Mes tâches impliquent ces exigences:

  1. Pour un produit particulier, évaluez si notre équipe logicielle peut mettre à niveau leur IDE et version JUnit sans perdre la compatibilité avec leurs projets existants.
  2. Développer des tests unitaires pour certains composants dans le Java (ce n'est pas Java en grande partie). Nous voulons convaincre l'équipe logicielle que les tests unitaires et TDD sont des outils inestimables qu'ils devraient utiliser. (Il y a actuellement une couverture de code de 0%.)
  3. D'une manière ou d'une autre, mettez fin aux jours de codage de cow-boy pour un système critique.

Après avoir obtenu une copie du code source, j'ai essayé de le construire et de l'exécuter, afin de comprendre ce que fait ce produit et comment il fonctionne. Je ne pouvais pas. J'ai demandé à mes superviseurs comment je fais et j'ai reçu une nouvelle machine autonome capable de la construire, y compris les scripts de construction qui le font réellement. Cela n'a pas fonctionné non plus, car comme ils auraient dû s'y attendre, leur code de production ne fonctionne que sur le système embarqué pour lequel il est conçu. Cependant, ils ont un simulateur à cet effet, ils ont donc obtenu le simulateur et l'ont mis sur cette machine pour moi. Le simulateur n'a pas fonctionné non plus. Au lieu de cela, j'ai finalement reçu une impression d'une interface graphique pour un écran particulier. Ils n'ont pas non plus de commentaires de code dans les 700 000+ Java LOC, ce qui le rend encore plus difficile à saisir. De plus, il y avait des problèmes pour évaluer si leurs projets étaient compatibles ou non avec les nouveaux IDE. En particulier , leur code ne s'est pas chargé correctement dans la très IDE version qu'ils utilisent.

Mon inventaire ressemble à ceci:

  • NetBeans 8, 9, 10, 11
  • JUnité 4, 5
  • Leur code source pour un produit particulier (comprend 700 000+ Java LOC)
  • Pratiquement aucun commentaire de code (parfois une signature)
  • Aucun test existant
  • Une photo physique d'une fenêtre GUI
  • Un document de conception de logiciel (109 p.) Qui ne traite pas du composant dans l'image

J'en ai au moins assez pour écrire théoriquement des tests qui peuvent s'exécuter. J'ai donc essayé un test unitaire de base sur ce composant. Cependant, je n'ai pas pu initialiser les objets qu'il avait en tant que dépendances, qui comprenaient des modèles, des gestionnaires et des connexions de base de données. Je n'ai pas beaucoup d'expérience JUnit au-delà des tests unitaires de base, alors suivez-moi à la section suivante.


Ce que j'ai appris de ma lecture

  1. Mocking: Si j'écris un test unitaire, il doit probablement avoir des variables fictives pour les dépendances de production que je ne peux pas facilement initialiser dans setUp.
  2. Tout le monde ici suggère généreusement le livre "Working Effectively with Legacy Code" de Michael Feathers.
  3. Les tests de régression sont probablement un bon point de départ. Je ne pense pas avoir suffisamment d'armes pour tenter des tests d'intégration, et les tests de régression fourniraient une satisfaction plus instantanée à notre équipe logicielle. Cependant, je n'ai pas accès à leurs bogues connus; mais, je pourrais éventuellement demander.

Et maintenant, une tentative d'articuler l'incertitude que j'ai encore comme question. Essentiellement, je ne comprends pas la partie de la façon d'écrire ces tests. En supposant que je ne reçois pas de conseils supplémentaires de la part de mes superviseurs (probablement), c'est dans mon but non seulement d'apprendre ce que fait ce composant mais de décider quels tests sont réellement utiles comme tests de régression.

En tant que professionnels qui ont travaillé sur des projets comme celui-ci plus longtemps que moi, pouvez-vous fournir des conseils sur la façon d'écrire des tests unitaires dans ce genre de situation?

8
Joshua Detwiler

En première approximation, les parties prenantes d'une suite de tests sont les développeurs/mainteneurs de code. Vous allez avoir besoin de leur temps. Insister là-dessus.

Demandez-leur quels sont les problèmes auxquels ils sont confrontés.
Demandez-leur quels sont les bogues qu'ils ont corrigés récemment (au cours des deux dernières années, en supposant que ce soit un long projet lent).

J'ai l'impression que vous ne vous attendez pas à ce qu'ils soient aimables pour votre travail; peut-être que tu as raison. Mais je ne pense pas que vous puissiez construire quelque chose d'utile sans leur aide.

Ils vont vous tenir la main en écrivant le premier test. Vous les tirerez peut-être, mais vous vous tiendrez par la main de toute façon.

Une fois que vous avez un test, j'espère que ce sera plus clair comment écrire le second. Si vous avez réussi à créer n'importe quel type de rapport, ce sera certainement plus clair.

10
ShapeOfMatter

Je vais supposer qu'à un moment donné, vous pouvez au moins compiler le code. Si vous ne pouvez même pas aller aussi loin, vous êtes en train de faire une folie.


L'absence d'exigences, de spécifications ou de captures d'écran appropriées n'est pas un bloqueur pour l'écriture de tests. Tant que vous pouvez lire le code source, vous pouvez écrire des tests.

Si vous êtes autorisé à refactoriser la base de code pour isoler des éléments tels que les connexions à la base de données derrière leur propre interface, il devient possible d'écrire des tests unitaires de boîte noire - essentiellement des tests pour lancer une entrée sur une méthode et affirmer son comportement ou sa sortie. Obtenez des tests couvrant chaque ligne de code en une seule méthode, puis demandez à l'un des membres les plus expérimentés de l'équipe d'examiner vos tests.

Si vous n'êtes pas autorisé à refactoriser la base de code afin d'écrire des tests unitaires, les tests d'intégration complète ou les tests d'automatisation de l'interface utilisateur sont votre seule option. Même dans ce cas, le test de la boîte noire est votre meilleure stratégie - lancez une entrée dans l'interface utilisateur et voyez comment elle réagit. Faites vos affirmations. Demandez à un membre senior de l'équipe de revoir vos tests.

À un moment donné, vous disposerez de suffisamment de tests automatisés pour pouvoir commencer à refactoriser la base de code en toute confiance pour introduire des tests unitaires. Les tests d'interface utilisateur garantissent le fonctionnement des principaux cas d'utilisation métier, et vous pouvez ensuite moderniser une architecture propice aux tests au niveau des unités ou des composants.

Un autre avantage des tests d'interface utilisateur est que vous pouvez bâtir une réputation auprès de votre équipe grâce à la compréhension de la base de code, ce qui à son tour les rend plus ouverts à l'introduction de modifications, car la preuve se trouve dans le pudding. Et vous aurez fait du pudding en écrivant des tests réussis.

En bref:

  • Écrire des tests Black Box sous forme de tests unitaires ou automatisés de l'interface utilisateur
  • Demandez aux membres seniors de revoir vos tests

Vous seriez surpris de la rapidité avec laquelle vous pouvez apprendre la vue d'ensemble d'une application de 700 000 lignes

3
Greg Burghardt

Sur la base de la description du problème et de vos commentaires, je pense que le mieux que vous puissiez faire est de commencer avec l'API Java et d'essayer de construire un test unitaire unique autour d'une méthode isolée.

Sans accès au code, je ne peux que vous donner des conseils limités mais je chercherais quelque chose qui a) n'a pas de dépendances b) ne change pas d'état. Par exemple. disons qu'il existe une méthode qui prend une valeur et vérifie qu'elle se situe dans une plage donnée. Si vous ne pouvez pas trouver quelque chose sans dépendances, essayez quelque chose qui récupère une valeur d'une dépendance et essayez de la simuler.

Une fois que vous avez trouvé quelque chose comme ça, vous pouvez commencer à construire des tests. Si la méthode teste une valeur positive, passez-la par un négatif et assurez-vous qu'elle l'attrape, etc. Le problème ici est que vous ne savez pas avec certitude quel est le comportement correct. Vous devrez peut-être demander de l'aide ou fouiller dans la documentation pour cela.

Il est peu probable que vous alliez très loin avec cela. La réalité est que l'écriture de code afin qu'il puisse être testé à l'unité est un art en soi. Le code qui n'a pas été écrit dans cet esprit peut être difficile à impossible à tester unitaire.

Une autre option qui peut être plus facilement implémentée est le test de compatibilité binaire. Autrement dit, vous capturez les entrées et les sorties d'un système, puis pour tester, vous alimentez ces mêmes entrées et comparez les sorties. Cela ne vous dira pas si le code est bon ou mauvais pour commencer, mais cela peut aider à détecter les erreurs de régression là où les choses ont changé involontairement lors d'une autre modification. Vous devrez cependant être en mesure d'exécuter l'intégralité de l'application pour que cela se produise.

1
JimmyJames