Comment puis-je utiliser Powermockito pour se moquer de la construction de nouveaux objets lors de l'essai d'une méthode dans une classe anonyme?
Je woud envie d'écrire un test Unitaire pour vérifier que le code ci-dessous utilise une BufferedInputStream:
public static final FilterFactory BZIP2_FACTORY = new FilterFactory() {
public InputStream makeFilter(InputStream in) {
//a lot of other code removed for clarity
BufferedInputStream buffer = new BufferedInputStream(in);
return new CBZip2InputStream(buffer);
}
};
(FilterFactory est une interface.)
Mon test donc loin ressemble à ceci:
@Test
public void testBZIP2_FactoryUsesBufferedInputStream() throws Throwable {
InputStream in = mock(InputStream.class);
BufferedInputStream buffer = mock(BufferedInputStream.class);
CBZip2InputStream expected = mock(CBZip2InputStream.class);
PowerMockito.spy(InputHelper.BZIP2_FACTORY); //This line fails
whenNew(BufferedInputStream.class).withArguments(in).thenReturn(buffer);
whenNew(CBZip2InputStream.class).withArguments(buffer).thenReturn(expected);
InputStream observed = InputHelper.BZIP2_FACTORY.makeFilter(in);
assertEquals(expected, observed);
}
L'appel à PowerMockito.espion, lève une exception avec ce message:
org.mockito.exceptions.base.MockitoException:
Mockito cannot mock this class: class edu.gvsu.cis.kurmasz.io.InputHelper$1
Mockito can only mock visible & non-final classes.
Que dois-je utiliser à la place de PowerMocktio.espion pour configurer les appels à whenNew?
OriginalL'auteur Zack | 2011-09-23
Vous devez vous connecter pour publier un commentaire.
Le message est assez évident: Vous ne pouvez pas se moquer de non-visible et final classes. Réponse courte : Créer une classe nommée de votre anonyme, et tester cette classe au lieu!
Longue réponse, nous allons creuser pourquoi !
Une classe anonyme est définitive
Vous instancier une classe anonyme de
FilterFactory
, lorsque le compilateur voit une classe anonyme, il crée un final et paquet visible classe. Donc, la classe anonyme est pas mockable par le biais de la norme moyenne c'est à dire par le biais de Mockito.Se moquant de la classe anonyme : possible, mais FRAGILE si pas HACKY
OK, maintenant, supposons que vous voulez être en mesure de se moquer de cette classe anonyme par Powermock. Compilateurs gcc pour compiler la classe anonyme avec le schéma suivant :
Se moquant de la classe anonyme possible, mais fragile (Et je le pense)
Donc, en supposant que la classe anonyme est la onzième être déclarées, il apparaît que
Vous êtes donc susceptible de se préparer pour le test de la classe anonyme:
Ce code compile, MAIS finira par être signalé comme une erreur avec votre IDE. L'IDE n'a probablement pas savoir à propos de
InputHelper$11.class
. IntelliJ qui n'utilise pas la classe compilée pour vérifier le code de rapport.Aussi le fait que l'anonyme de nommage des classes dépend en réalité de l'ordre de la déclaration est un problème, lorsque quelqu'un ajoute une autre classe anonyme avant, la numérotation pourrait changer.
Anonyme classes sont faites pour rester anonyme, si le compilateur gars décident un jour d'utiliser des lettres ou même aléatoire identifiants!
Si moqueur anonyme classes par le biais de Powermock est possible, mais fragile, ne le faites jamais à un vrai projet!
ÉDITÉ REMARQUE : L'Éclipse compilateur a un schéma de numérotation différent, il utilise toujours un nombre à 3 chiffres :
Aussi, je ne pense pas que le JLS préciser clairement comment les compilateurs doivent nom anonyme classes.
Vous n'avez pas réaffecter l'espion au champ statique
PowerMockito.spy
renvoie à l'espion, il ne change pas la valeur deInputHelper.BZIP2_FACTORY
. Donc, vous devez effectivement réglés via la réflexion en ce domaine. Vous pouvez utiliser leWhitebox
utilitaire qui Powermock fournir.Conclusion
Trop de mal pour le tester juste avec se moque que l'anonyme filtre utilise un
BufferedInputStream
.Alternative
Je préfère écrire le code suivant:
Une entrée helper qui va utiliser le nom de la classe, je n'utilise pas le nom de l'interface pour faire comprendre à l'utilisateur quel est le but de ce filtre!
Et maintenant le filtre lui-même :
Maintenant, vous pouvez écrire un test comme ceci :
Mais pourrait éventuellement éviter powermock choses pour ce scénario de test si vous forcez la
CBZip2InputStream
pour n'accepter que lesBufferedInputStream
. Généralement à l'aide d'Powermock signifie que quelque chose est incorrect avec le design. À mon avis Powermock est idéal pour l'héritage de logiciels, mais peut aveugler les développeurs lors de la conception d'un nouveau code; qu'ils manquent le point de la programmation orientée objet est une bonne partie, je dirais même qu'ils sont en train de concevoir le code de legs.Espère que ça aide !
Ouais je suis d'accord, mais dans ce cas, la classe anonyme est assez simple et ne doit pas avoir besoin de ces lourds de la configuration du test. Dans de Zack cas, il semble qu'il s'agit de plus de code à l'intérieur de la classe. En fin de compte, cela dépend de la criticité de ce qui est testé, s'il est jugé important que le code lui-même est remarquable à ce sujet.
Grâce à vous, je me rends compte que j'ai manquer de connaissances important. J'utilise utils (ou aide) classe de réduire le code pour la création et le test de la classe abstraite et bien sûr, je veux espionner. C'était l'erreur, alors maintenant, j'ai créer simple extension de la classe dans les sources de test et spy ce lieu et son ok
OriginalL'auteur Brice
Vieux post, mais vous n'avez pas besoin de créer une classe nommée - utiliser des caractères génériques plutôt que comme indiqué dans ce post powermock moqueur constructeur via whennew() ne fonctionne pas avec la classe anonyme
@PrepareForTest(fullyQualifiedNames = "com.yourpackage.containing.anonclass.*")
@PrepareForTest(fullyQualifiedNames = "com.yourpackage.YourClass*")
OriginalL'auteur Matt Byrne
Je vient de autour de le même problème. Ainsi, selon le la documentation du constructeur moqueur vous avez besoin pour préparer la classe, ce qui permettra de créer le mal de classe(es). Dans votre cas, le mal de classes sont BufferedInputStream et CBZip2InputStream, et le créateur est une classe anonyme, qui ne peut être définie dans PrepareForTest annotation. J'ai donc dû faire la même chose comme vous l'avez fait (hmm, juste vu votre commentaire), je déplacé de la classe anonyme pour la classe nommée.
OriginalL'auteur Gábor Lipták
Vous devez exécuter le test à l'aide de la PowerMockito coureur, et vous devez en informer le cadre de la classe(es) doivent avoir personnalisé comportement. Ajouter la classe suivante annotations sur votre classe de test:
OriginalL'auteur Morten Lauritsen Khodabocus