junit & java: test de méthodes non publiques

JUnit ne testera que les méthodes publiques de ma classe. Comment est-ce que je fais des tests de junit sur ceux qui ne le sont pas (c’est-à-dire privés, protégés)?

Je peux les tester en n’utilisant pas Junit, mais je me demandais quelle était la méthode standard de Junit.

Une école de pensée sur les tests unitaires indique que vous ne devriez pouvoir tester que des méthodes publiques, car vous ne devriez tester que votre API publique et, ce faisant, vous devriez couvrir le code dans vos méthodes non publiques. Votre kilométrage peut varier; Je trouve que c’est parfois le cas et parfois pas.

Cela dit, il existe plusieurs manières de tester des méthodes non publiques:

  • Vous pouvez tester les méthodes de protection et de scope du package en plaçant vos tests unitaires dans le même package que les classes qu’ils testent. C’est une pratique assez courante.
  • Vous pouvez tester les méthodes protégées à partir des tests unitaires dans un autre package en créant une sous-classe de la classe testée qui remplace les méthodes que vous souhaitez tester en tant que public. Ces méthodes remplacées appellent les méthodes d’origine avec le mot clé super. En règle générale, cette “sous-classe testing” serait une classe interne de la classe JUnit TestCase effectuant les tests. C’est un peu plus pirate, à mon avis, mais je l’ai fait.

J’espère que cela t’aides.

Comme avec de nombreux problèmes de tests unitaires, tester des méthodes privées est en fait un problème de conception déguisé. Plutôt que d’essayer de faire quelque chose de délicat pour tester des méthodes privées, quand je souhaite rédiger des tests pour des méthodes privées, je prends une minute pour me demander: “Comment pourrais-je concevoir cela pour pouvoir le tester à travers des méthodes publiques?”

Si cela ne fonctionne pas, JUnitX permet de tester des méthodes privées, bien que je pense qu’il est uniquement disponible pour JUnit 3.8.

Lorsque vous écrivez un test JUnit, vous devez effectuer un changement d’esprit subtil: «Je suis un client de mon propre cours maintenant. Cela signifie que privé est privé et que vous testez uniquement le comportement du client.

Si la méthode devait vraiment être privée, je la considérerais comme un défaut de conception pour la rendre visible uniquement à des fins de test. Vous devez pouvoir déduire son bon fonctionnement en fonction de ce que le client voit.

Au cours des trois années écastings depuis que j’ai écrit cela, j’ai commencé à aborder le problème légèrement différemment, en utilisant la reflection Java.

Le sale petit secret est que vous pouvez tester des méthodes privées dans JUnit comme vous le feriez pour des méthodes publiques, en utilisant la reflection. Vous pouvez tester le contenu de votre cœur et ne pas les exposer publiquement aux clients.

La solution la plus simple consiste à placer les tests JUnit dans le même package (mais dans un répertoire différent) et à utiliser la visibilité par défaut (c’est-à-dire un paquet privé) pour les méthodes.

Une autre approche plus compliquée consiste à utiliser la reflection pour accéder aux méthodes privées.

Si vous avez une quantité importante de logique enfouie sous un nombre relativement faible de points d’entrée “publics”, vous violez probablement le principe de la responsabilité unique . Si possible, vous aurez besoin de refactoriser le code en plusieurs classes, pour aboutir à des méthodes plus “publiques” à tester.

Voici la méthode “probablement ne devrait pas le faire de cette façon” que tout le monde ne cesse de vous reprocher. Je pense que c’est certainement une possibilité de le faire de cette manière. Le code suivant accédera à un champ privé, mais le code d’une méthode privée est presque identique.

public void testPrivateField() throws InterruptedException { Class clazz = ClassWPrivateField.class; try { Field privateField = clazz.getDeclaredField("nameOfPrivateField"); privateField.setAccessible(true); // This is the line // do stuff } catch(NoSuchFieldException nsfe) { nsfe.printStackTrace(); fail(); } catch(IllegalAccessException iae) { iae.printStackTrace(); fail(); } } 

J’utilise presque toujours Spring dans mes projets Java et mes objects sont conçus pour l’dependency injections. Ils ont tendance à être des implémentations assez granulaires d’interfaces publiques assemblées dans le contexte de l’application. En tant que tel, j’ai rarement (voire jamais) besoin de tester des méthodes privées car la classe elle-même est suffisamment petite pour ne pas poser de problème.

Même lorsque je n’utilise pas Spring, j’ai tendance à adopter les mêmes pratiques d’assemblage d’objects petits et simples en abstractions de plus en plus grandes, chacune étant relativement simple mais rendue complexe par les objects agrégés.

D’après mon expérience, le besoin de tester des méthodes privées est un indicateur que ce que vous êtes en train de tester pourrait (et devrait) être simplifié.

Cela étant, si vous ressentez toujours le besoin:

  • Les méthodes protégées peuvent être testées par des sous-classes;
  • Les méthodes privées de package peuvent être testées en plaçant les tests unitaires dans le même package; et
  • Les méthodes privées peuvent être testées par unité en fournissant, par exemple, une méthode proxy de fabrique privée de package. Pas idéal mais privé signifie privé.

En général, vous ne testez pas les méthodes privées car elles ne peuvent (normalement) être testées qu’indirectement via une autre méthode publique. Lorsque vous testez la conduite et que vous créez des méthodes privées, elles sont généralement le résultat d’une refactorisation par “méthode d’extrait” et sont déjà testées indirectement.

Si vous êtes soucieux de tester une méthode privée avec beaucoup de logique, la meilleure chose à faire est de déplacer ce code dans une autre classe dans une méthode publique. Une fois que vous avez fait cela, la méthode précédente qui utilise ce code peut avoir ses tests simplifiés en ayant les fonctionnalités fournies par un stub ou un simulacre.

Ce n’est pas vraiment une réponse, plus que je suis tombé sur le même problème, et “si ça doit être privé, ça devrait probablement être refactorisé” ne me convient pas.

Supposons que vous ayez une sorte de fonctionnalité que vous souhaitez séparer d’une manière ou d’une autre interne à la classe. Par exemple, supposons que j’ai quelque chose comme ceci:

 public class HolderOfSomeSsortingngs{ private List internal_values; public List get() { List out = new ArrayList(); for (Ssortingng s:internal_values) { out.add(process(s)); } return get; } private static Ssortingng process(Ssortingng input) { //do something complicated here that other classes shouldn't be interestd in } } 

Le point ici est que JUNIT me force à rendre le processus public, ou au moins protégé, ou à le mettre dans sa propre classe d’utilitaire. Mais si c’est une sorte de logique interne de HolderOfSomeSsortingngs, ce n’est pas du tout clair pour moi que c’est correct – il me semble que cela devrait être privé, et le rendre plus visible gâche le code d’une certaine manière.

Pour emprunter une pensée à Andy Hunt, même vos méthodes privées doivent avoir un effet secondaire qui vous intéresse. En d’autres termes, elles doivent être appelées par une méthode publique et effectuer une tâche intéressante entraînant la modification de l’état de votre object. . Test pour ce changement d’état.

Supposons que vous ayez une méthode publique pubMethod et une méthode privée privMethod. Lorsque vous appelez pubMethod, il appelle à son tour privMethod pour effectuer une tâche (peut-être parsingr une chaîne). PubMethod utilise ensuite cette chaîne analysée pour définir les valeurs des variables membres ou pour influencer sa propre valeur de retour. Testez en recherchant l’effet souhaité sur la valeur de retour de pubMethod ou sur les variables membres (éventuellement en utilisant des accesseurs pour y accéder).

DP4j Jar

Pour tester des méthodes privées, nous devons utiliser la reflection et ses réponses dans toutes les réponses.

Eh bien maintenant, cette tâche est simplifiée avec l’aide de Dp4j jar.

  • Dp4j parsing votre code et génère automatiquement le code de l’API Reflection pour vous.

  • Ajoutez simplement dp4j.jar à votre CLASSPATH.

  • Dp4j.jar contient des processeurs d’annotation, ils rechercheront les méthodes de votre code annotées avec l’annotation @Test JUnit.

  • Dp4j parsing le code de ces méthodes, et s’il trouve que vous accédez illégalement à des méthodes privées, il remplacera votre référence de méthode privée non valide par un code équivalent utilisant l’API Reflection de Java.

Obtenez plus de détails ici

Vous pouvez utiliser TestNG au lieu de JUnit, qui ne se soucie pas de la méthode privée ou publique.

Utilisez la reflection comme ci-dessus pour tester les méthodes privées. Si nous suivons TDD, nous devrions tester les méthodes privées, car TDD implique qu’il n’y aura pas de sursockets plus tard. Donc, il ne faut pas attendre pour finir sa méthode publique pour tester les soldats. Et cela aide à des tests de régression plus granulaires lors de la ré-affacturage.

Recherchez “PrivateAccessor.invoke”. Mon code l’importe de “junitx.util”, mais je ne sais pas d’où ça vient.

En accord avec à peu près tous les articles – vous devriez probablement réorganiser et probablement pas tester le privé sauf par le biais du public, je voulais juste append une façon différente de penser à cela …

Considérez votre classe elle-même comme une “Unité”, pas une méthode. Vous testez la classe et pouvez conserver un état valide quelle que soit la manière dont ses méthodes publiques sont appelées.

L’appel de méthodes privées peut détruire l’encapsulation et invalider les tests.

En quelque sorte, et je ne suis pas sûr de savoir où tout le monde aborde le problème de la “programmation polyglotte”, mais les tests Groovy sont exécutables dans Junit, et ignorent tout le problème public / non-public. Côté note, il a été officiellement classé comme un “bug”, mais quand ils ont essayé de le réparer, il y a eu une telle tempête qu’il a été remis tel quel.

Je suis absolument d’accord avec @duffymo que ce code devrait être testé du sharepoint vue du client (même s’il dit qu’il a cessé de penser de cette façon). Cependant, de ce sharepoint vue, les relations entre les particuliers et les autres ont des significations différentes. Le client d’une méthode privée est la classe elle-même, alors je préfère les tester via l’API externe (publique / protégée par paquet). Cependant, les membres protégés et protégés sont disponibles pour les clients externes. Je les teste donc avec des faux héritant de la classe propriétaire ou résidant dans le même package.