95 votes

Est-ce une mauvaise pratique de l'utilisation de la Réflexion dans les tests Unitaires?

Durant les dernières années, j'ai toujours pensé que dans Java, la Réflexion est largement utilisé pendant le test Unitaire. Depuis quelques variables/méthodes qui doivent être vérifiés dans le privé, c'est en quelque sorte nécessaire pour lire les valeurs. J'ai toujours pensé que la Réflexion, de l'API est également utilisé à cette fin.

La semaine dernière j'ai eu à tester des paquets et donc écrire des tests JUnit. Comme toujours, j'ai utilisé de Réflexion pour accéder aux champs privés et des méthodes. Mais mon directeur de thèse, qui a vérifié le code n'était pas vraiment heureuse et m'a dit que le Reflet de l'API n'était pas destiné à l'utilisation pour de tels "piratage". Au lieu de cela, il a proposé de modifier la visibilité dans le code de production.

Est-ce vraiment une mauvaise pratique de l'utilisation de la Réflexion? Je ne peux pas vraiment croire que l'-

Edit: je devrais avoir mentionné le fait que j'était nécessaire que tous les tests sont dans un paquet séparé appelé test (donc protégés à l'aide de la visibilité par exemple, n'était pas une solution possible aussi)

77voto

Péter Török Points 72981

À mon humble avis, la Réflexion devrait vraiment être un dernier recours, réservés pour le cas particulier de l'unité de test de code de legs ou d'une API, vous ne pouvez pas changer. Si vous testez votre propre code, le fait que vous devez utiliser la Réflexion signifie que votre conception n'est pas testable, de sorte que vous devrait corriger cela, au lieu de recourir à la Réflexion.

Si vous devez accéder aux membres privés dans vos tests unitaires, cela signifie généralement que la classe en question a une mauvaise interface, et/ou essaie d'en faire trop. Donc, soit son interface doit être révisé ou du code doit être extrait dans une catégorie distincte, où ces problématiques, des méthodes de champ accesseurs peuvent être rendus publics.

Notez que l'utilisation de la Réflexion en général des résultats dans le code qui, en plus d'être plus difficile à comprendre et à maintenir, est aussi plus fragile. Il y a toute une série d'erreurs qui, dans le cas normal serait détectée par le compilateur, mais avec de la Réflexion, ils surgissent comme des exceptions d'exécution seulement.

Mise à jour: @tackline noté, cela ne concerne que l'utilisation de la Réflexion au sein de son propre code de test, pas de l'intérieur de la structure de test. JUnit (et probablement tous les autres cadres similaires) utilise la réflexion pour identifier et appeler vos méthodes d'essai - c'est un justifiée et a localisé l'usage de la réflexion. Il serait difficile, voire impossible, de fournir les mêmes fonctionnalités et la commodité sans l'aide de la Réflexion. Otoh, que c'est complètement encapsulé dans le cadre de la mise en œuvre, afin de ne pas compliquer ou de compromettre nos propres tests de code.

64voto

Bozho Points 273663

C'est vraiment mauvais pour modifier la visibilité d'une production de l'API juste pour le plaisir de tester. Que la visibilité est susceptible d'être mise à sa valeur actuelle, pour des raisons valables et ne doit pas être changé.

L'utilisation de la réflexion pour les tests unitaires est la plupart du temps très bien. Bien sûr, vous devez concevoir vos classes pour la testabilité, de sorte que moins de réflexion est nécessaire.

Le printemps, par exemple, a ReflectionTestUtils. Mais son but est de définir les objets fantaisie, de dépendances, d'où le printemps était censé injecter.

Le sujet est plus profond que "faire et ne pas faire", et en ce qui concerne ce qui doit être testé, si l'état interne des objets doit être testé ou pas; si l'on doit se permettre de questionner la conception de la classe sous test; etc.

30voto

Carl Manaster Points 23696

Du point de vue de TDD - Test Driven Design - c'est une mauvaise pratique. Je sais que vous ne l'étiquette ce TDD, ni poser des questions sur elle, mais TDD est une bonne pratique et cela va à l'encontre de son grain.

En mode TDD, nous utilisons nos tests pour définir l'interface de la classe - le public de l'interface et par conséquent, nous sommes en train d'écrire des tests qui interagissent directement avec l'interface publique. Nous sommes inquiets que l'interface des niveaux d'accès appropriés sont une partie importante de la conception, une partie importante du code de qualité. Si vous vous trouvez avoir besoin de tester quelque chose de privé, il est généralement, dans mon expérience, une conception de l'odorat.

7voto

Yishai Points 42417

Je la considère comme une mauvaise pratique, mais juste changer la visibilité dans le code de production n'est pas une bonne solution, vous avez à regarder la cause. Soit vous avez un invérifiables API (qui est l'API n'expose pas assez pour un test pour obtenir une poignée sur) si vous êtes à la recherche de test privé de l'etat à la place, ou vos tests sont trop couplé avec votre mise en œuvre, qui fera d'eux de marginal, et utilisez-les lorsque vous refactoriser.

Sans en savoir plus sur votre cas, je ne peux pas vraiment en dire plus, mais il est en effet considéré comme une mauvaise pratique d'utiliser la réflexion. Personnellement, je préfère faire le test statique intérieur de la classe de la classe sous test que le recours à la réflexion (si dire la invérifiables partie de l'API n'était pas sous mon contrôle), mais certains endroits sera un plus grand problème avec le code de test étant dans le même package que le code de production qu'avec l'aide de la réflexion.

EDIT: En réponse à votre travail d'édition, qui est au moins aussi mauvais que de l'aide de la Réflexion, probablement pire. La façon dont il est habituellement traitée est d'utiliser le même paquet, mais garder les tests dans une structure de répertoire séparé. Si les tests unitaires ne vont pas dans le même package que la classe sous test, je ne sais pas quoi.

De toute façon, vous pouvez toujours contourner ce problème en utilisant protégées (malheureusement pas de colis-privé qui est vraiment idéal pour cela) par le test d'une sous-classe de la sorte:

 public class ClassUnderTest {
      protect void methodExposedForTesting() {}
 }

Et à l'intérieur de votre unité de test

class ClassUnderTestTestable extends ClassUnderTest {
     @Override public void methodExposedForTesting() { super.methodExposedForTesting() }
}

Et si vous avez protégé constructeur:

ClassUnderTest test = new ClassUnderTest(){};

Je ne recommande pas nécessairement la ci-dessus pour les situations normales, mais les restrictions qu'on vous demande de travailler sous et pas de "meilleures pratiques" déjà.

6voto

simpatico Points 2730

Je seconde la pensée de Bozho:

Il est vraiment mauvais pour modifier la visibilité d'une production de l'API juste pour le plaisir de tester.

Mais si vous essayez de faire la chose la plus simple qui pourrait éventuellement fonctionner , alors qu'il pourrait être préféré à l'écriture de la Réflexion de l'API standard, au moins tant que votre code est encore nouveau/en train de changer. Je veux dire, le fardeau de modifier manuellement la réflexion des appels à partir de votre test à chaque fois que vous modifiez le nom de la méthode, ou params, est à mon humble avis trop de charge et le mauvais accent.

Étant tombé dans le piège de la détente, de l'accessibilité juste pour le test et ensuite par inadvertance accès à l'a-privé de la méthode de production de code que j'ai pensé dp4j.jar: il injecte le Reflet code de l'API (Lombok-style), de sorte que vous ne modifiez pas le code de production ET de ne pas écrire de l'API Reflection vous-même; dp4j remplace votre accès direct dans l'unité de test avec l'équivalent de réflexion de l'API au moment de la compilation. Voici un exemple de dp4j avec JUnit.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X