J'utilise Mockito 1.9.0. Je veux simuler le comportement d'une seule méthode d'une classe dans un test JUnit, donc j'ai
final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);
Le problème est que, dans la deuxième ligne, myClassSpy.method1()
est en train d'être appelé, ce qui entraîne une exception. La seule raison pour laquelle j'utilise des simulacres est que, plus tard, à chaque appel de myClassSpy.method1()
, la méthode réelle ne sera pas appelée et l'objet myResults
sera renvoyé.
MyClass
est une interface et myInstance
est une implémentation de cela, si cela compte.
Que dois-je faire pour corriger ce comportement d'espionnage?
Permettez-moi de citer la documentation officielle :
Important getcha sur l'espionnage d'objets réels!
Parfois, il est impossible d'utiliser (Object) pour écraser des espions. Exemple:
List list = new LinkedList();
List spy = spy(list);
// Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");
// You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);
Dans votre cas, cela ressemble à quelque chose comme:
doReturn(resulstIWant).when(myClassSpy).method1();
Mon cas était différent de la réponse acceptée. J'essayais de me moquer d'une méthode package-private pour une instance qui ne résidait pas dans ce package
package common;
public class Animal {
void packageProtected();
}
package instances;
class Dog extends Animal { }
et les classes de test
package common;
public abstract class AnimalTest<T extends Animal> {
@Before
setup(){
doNothing().when(getInstance()).packageProtected();
}
abstract T getInstance();
}
package instances;
class DogTest extends AnimalTest<Dog> {
Dog getInstance(){
return spy(new Dog());
}
@Test
public void myTest(){}
}
La compilation est correcte, mais lorsqu'elle tente de configurer le test, elle appelle la méthode réelle.
Déclarer la méthode protected ou public corrige le problème, même si ce n'est pas une solution propre.
La réponse de Tomasz Nurkiewicz semble ne pas raconter toute l'histoire!
NB version Mockito: 1.10.19.
Je suis vraiment un mockito newb, je ne peux donc pas expliquer le comportement suivant: s'il existe un expert capable d'améliorer cette réponse, n'hésitez pas.
La méthode en question ici, getContentStringValue
, estPASfinal
etPASstatic
.
Cette ligne appelle appelle la méthode d'origine getContentStringValue
:
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));
Cette ligne n'appelle pas appelle la méthode d'origine getContentStringValue
:
doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));
Pour des raisons auxquelles je ne peux pas répondre, l'utilisation de isA()
entraîne l'échec du comportement souhaité de la méthode "Ne pas appeler de méthode" de doReturn
.
Regardons les signatures de méthodes impliquées ici: ce sont toutes deux des méthodes static
de Matchers
. La Javadoc dit que tous les deux retournent null
, ce qui est un peu difficile à comprendre. Vraisemblablement, l'objet Class
passé en tant que paramètre est examiné, mais le résultat n'est jamais calculé, ni ignoré. Étant donné que null
peut représenter n'importe quelle classe et que vous espérez que la méthode fausse ne sera pas appelée, les signatures de isA( ... )
et any( ... )
ne peuvent-elles que renvoyer null
au lieu d'un paramètre générique * <T>
?
En tous cas:
public static <T> T isA(Java.lang.Class<T> clazz)
public static <T> T any(Java.lang.Class<T> clazz)
La documentation de l'API ne donne aucune idée à ce sujet. Il semble également dire que la nécessité d'un tel comportement de "méthode de non-appel" est "très rare". Personnellement, j'utilise cette technique tout le temps : en général, je trouve que se moquer implique quelques lignes qui "définissent la scène" ... suivi de l'appel d'une méthode qui "reproduit" ensuite la scène dans le contexte factice ont mis en scène ... et pendant que vous montez le décor et les accessoires, la dernière chose que vous voulez, c'est que les acteurs entrent sur scène et commencent à jouer leur coeur ...
Mais c’est bien au-delà de mon niveau de salaire… J'invite les explications de tous les grands prêtres Mockito qui passent….
* "paramètre générique" est-il le bon terme?
Dans mon cas, avec Mockito 2.0, je devais changer tous les paramètres any () en nullable () afin de remplacer le véritable appel.
Un autre scénario possible pouvant causer des problèmes avec les espions est le cas où vous testez haricots printaniers (avec framework de test printanier) ou un autre framework permettant de représenter vos objets pendant le test .
Exemple
@Autowired
private MonitoringDocumentsRepository repository
void test(){
repository = Mockito.spy(repository)
Mockito.doReturn(docs1, docs2)
.when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}
Dans le code ci-dessus, Spring et Mockito essaieront d’alimenter votre objet MonitoringDocumentsRepository par proxy, mais Spring sera le premier, ce qui provoquera un véritable appel de la méthode findMonitoringDocuments. Si nous déboguons notre code juste après avoir mis un espion sur un objet de référentiel, il ressemblera à ceci dans le débogueur:
repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$
@SpyBean à la rescousse
Si à la place l'annotation @Autowired
est utilisée l'annotation @SpyBean
, nous résoudrons le problème ci-dessus, l'annotation SpyBean injectera également un objet de référentiel, mais ce sera d'abord traité par Mockito et ressemblera à ceci à l'intérieur du débogueur.
repository = MonitoringDocumentsRepository$$MockitoMock$$EnhancerBySpringCGLIB$
et voici le code:
@SpyBean
private MonitoringDocumentsRepository repository
void test(){
Mockito.doReturn(docs1, docs2)
.when(repository).findMonitoringDocuments(Mockito.nullable(MonitoringDocumentSearchRequest.class));
}
J'ai trouvé encore une autre raison pour que Spy appelle la méthode d'origine.
Quelqu'un a eu l'idée de se moquer d'une classe final
et a trouvé environ MockMaker
:
Comme cela fonctionne différemment de notre mécanisme actuel et que celui-ci a différentes limites, et comme nous souhaitons recueillir l'expérience et les commentaires des utilisateurs, cette fonctionnalité devait être explicitement activée pour être disponible. cela peut être fait via le mécanisme d'extension mockito en créant le fichier
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
contenant une seule ligne:mock-maker-inline
Après avoir fusionné et apporté ce fichier sur ma machine, mes tests ont échoué.
Il me suffisait de supprimer la ligne (ou le fichier) et spy()
fonctionnait.
Réponse pour les utilisateurs de scala: Même mettre doReturn
en premier ne fonctionne pas! Voir ce post .
Une façon de s’assurer qu’une méthode d’une classe n’est pas appelée consiste à remplacer la méthode par un mannequin.
WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
@Override
public void select(TreeItem i) {
log.debug("SELECT");
};
});