Je travaille à rendre mes classes testables à l'unité, en utilisant l'injection de dépendance. Mais certaines de ces classes ont beaucoup de clients, et je ne suis pas encore prêt à les refactoriser pour commencer à passer dans les dépendances. J'essaie donc de le faire progressivement; en conservant les dépendances par défaut pour l'instant, mais en les autorisant à être remplacées pour les tests.
Une approche que je conise consiste simplement à déplacer tous les «nouveaux» appels dans leurs propres méthodes, par exemple:
public MyObject createMyObject(args) {
return new MyObject(args);
}
Ensuite, dans mes tests unitaires, je peux simplement sous-classer cette classe et remplacer les fonctions de création, afin qu'elles créent de faux objets à la place.
Est-ce une bonne approche? Y a-t-il des inconvénients?
Plus généralement, est-il acceptable d'avoir des dépendances codées en dur, tant que vous pouvez les remplacer pour les tests? Je sais que l'approche préférée est de les exiger explicitement dans le constructeur, et j'aimerais y arriver éventuellement. Mais je me demande si c'est une bonne première étape.
Un inconvénient qui m'est venu à l'esprit: si vous avez de vraies sous-classes que vous devez tester, vous ne pouvez pas réutiliser la sous-classe de test que vous avez écrite pour la classe parente. Vous devrez créer une sous-classe de test pour chaque sous-classe réelle, et elle devra remplacer les mêmes fonctions de création.
Réponses:
C'est une bonne approche pour commencer. Notez que le plus important est de couvrir votre code existant avec des tests unitaires; une fois que vous avez les tests, vous pouvez refaçonner plus librement pour améliorer encore votre conception.
Donc, le point initial n'est pas de rendre le design élégant et brillant - juste de rendre l'unité de code testable avec les changements les moins risqués . Sans tests unitaires, vous devez être extrêmement conservateur et prudent pour éviter de casser votre code. Ces modifications initiales peuvent même rendre le code plus maladroit ou laid dans certains cas - mais si elles vous permettent d'écrire les premiers tests unitaires, vous pourrez éventuellement refactoriser vers la conception idéale que vous avez en tête.
Le travail fondamental à lire sur ce sujet est Travailler efficacement avec le code hérité . Il décrit l'astuce que vous montrez ci-dessus et bien d'autres, en utilisant plusieurs langues.
la source
Les gens de Guice recommandent d'utiliser
pour toutes les propriétés au lieu d'ajouter simplement @Inject à leur définition. Cela vous permettra de les traiter comme des beans normaux, ce que vous pourrez
new
lors des tests (et définir les propriétés manuellement) et @Inject en production.la source
Lorsque je suis confronté à ce type de problème, je vais identifier chaque dépendance de ma classe à tester et créer un constructeur protégé qui me permettra de les transmettre. C'est en fait la même chose que de créer un setter pour chacun, mais je préfère déclarer des dépendances dans mes constructeurs pour ne pas oublier de les instancier à l'avenir.
Donc ma nouvelle classe testable unitaire aura 2 constructeurs:
la source
Vous voudrez peut-être considérer l'idiome «getter crée» jusqu'à ce que vous puissiez utiliser la dépendance injectée.
Par exemple,
Cela vous permettra de refactoriser en interne afin de pouvoir référencer votre myObject via le getter. Vous n'avez jamais à appeler
new
. À l'avenir, lorsque tous les clients seront configurés pour autoriser l'injection de dépendances, tout ce que vous aurez à faire sera de supprimer le code de création en un seul endroit - le getter. Le setter vous permettra d'injecter des objets fictifs au besoin.Le code ci-dessus n'est qu'un exemple et il expose directement l'état interne. Vous devez examiner attentivement si cette approche est appropriée pour votre base de code.
Je vous recommande également de lire « Travailler efficacement avec Legacy Code » qui contient une multitude de conseils utiles pour réussir une refactorisation majeure.
la source