J'ai récemment terminé un refactoring boîte noire. Je ne parviens pas à m'enregistrer, car je n'arrive pas à déterminer comment le tester.
À un niveau élevé, j'ai une classe dont l'initialisation implique de récupérer des valeurs d'une classe B. Si la classe B est "vide", elle génère des valeurs par défaut sensibles. J'ai extrait cette partie d'une méthode qui initialise la classe B à ces mêmes valeurs par défaut.
Je n'ai pas encore déterminé le but/le contexte de l'une ou l'autre classe, ni la manière dont ils seraient utilisés. Je ne peux donc pas initialiser l'objet à partir d'une classe B vide et vérifier qu'il a les bonnes valeurs/fait la bonne chose.
Ma meilleure idée est d'exécuter le code d'origine, le code dur dans les résultats des méthodes publiques en fonction des membres initialisés, et de tester le nouveau code par rapport à cela. Je ne peux pas vraiment expliquer pourquoi je me sens vaguement mal à l'aise avec cette idée.
Y a-t-il une meilleure attaque ici?
Vous allez bien!
La création de tests de régression automatisés est souvent la meilleure chose que vous puissiez faire pour rendre un composant refactorisable. Cela peut être surprenant, mais de tels tests peuvent souvent être écrits sans la pleine compréhension de ce que le composant fait en interne, tant que vous comprenez les "interfaces" d'entrée et de sortie (au sens général de ce mot). Nous l'avons fait plusieurs fois dans le passé pour des applications héritées complètes, pas seulement pour des cours, et cela nous a souvent aidés à éviter de casser des choses que nous ne comprenions pas complètement.
Cependant, vous devez disposer de suffisamment de données de test et vous assurer de bien comprendre ce que fait le logiciel du point de vue d'un utilisateur de ce composant, sinon vous risquez d'omettre des cas de test importants.
C'est IMHO une bonne idée de mettre en œuvre vos tests automatisés avant vous commencez le refactoring, pas après, donc vous pouvez faire le refactoring en petites étapes et vérifier chaque étape. Le refactoring lui-même devrait rendre le code plus lisible, il vous aide donc à améliorer votre compréhension des éléments internes petit à petit. Ainsi, les étapes de commande dans ce processus sont
Une raison importante pour écrire des tests unitaires est qu'ils documentent l'API du composant d'une manière ou d'une autre. Ne pas comprendre l'objectif du code en cours de test est vraiment un problème ici. La couverture du code est un autre objectif important, difficile à atteindre sans savoir quelles branches d'exécution existent et comment sont-elles déclenchées.
Cependant, s'il est possible de réinitialiser l'état proprement (ou de construire le nouvel objet de test à chaque fois), on peut écrire des tests de type "corbeille dans la corbeille" qui alimentent principalement une entrée aléatoire du système et observent la sortie.
Ces tests sont difficiles à maintenir, car lorsqu'ils échouent, il peut être difficile de dire pourquoi et à quel point ils sont graves. La couverture peut être discutable. Cependant, ils sont encore bien meilleurs que rien. Lorsqu'un tel test échoue, le développeur peut réviser les dernières modifications avec plus d'attention et, espérons-le, y repérer le bogue.