Mockito: Essayer d'espionner méthode appelle la méthode originale

J'utilise Mockito 1.9.0. Je veux simuler le comportement d'une méthode unique d'une classe de test JUnit, j'ai donc

final MyClass myClassSpy = Mockito.spy(myInstance);
Mockito.when(myClassSpy.method1()).thenReturn(myResults);

le problème est, dans la deuxième ligne, myClassSpy.method1() est effectivement appelé, résultant en une exception. La seule raison pour laquelle j'utilise Mock est que plus tard, à chaque fois que myClassSpy.method1() est appelé, la méthode réelle ne sera pas appelée et l'objet myResults sera retourné.

MyClass est une interface et myInstance est une mise en œuvre de cela, si cela importe.

Que dois-je faire pour corriger ce espionnage de comportement?

238
demandé sur adam_0 2012-07-24 00:35:02

7 réponses

Permettez-moi de citer la documentation officielle :

gotcha Important sur l'espionnage des objets réels!

parfois, il est impossible d'utiliser quand(objet) pour les 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, il va quelque chose comme:

doReturn(resulstIWant).when(myClassSpy).method1();
420
répondu Tomasz Nurkiewicz 2018-01-20 04:14:22

mon cas était différent de la réponse acceptée. J'essayais de me moquer d'une méthode paquet-privé pour une instance qui ne vivait pas dans ce paquet

package common;

public class Animal {
  void packageProtected();
}

package instances;

class Dog extends Animal { }

et les classes d'essai

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 quand elle essaye de configurer le test, elle invoque la méthode réelle à la place.

déclarant la méthode protégé ou public fixe problème, tho ce n'est pas une solution propre.

21
répondu Maragues 2016-08-29 08:17:57

la réponse de Tomasz Nurkiewicz semble ne pas raconter toute l'histoire!

NB Mockito version: 1.10.19.

je suis très Mockito newb, donc je ne peux pas expliquer le comportement suivant: s'il ya un expert là-bas qui peut améliorer cette réponse, s'il vous plaît se sentir libre.

la méthode en question ici, getContentStringValue , est pas final et pas static .

Cette ligne ne appel de la méthode originale getContentStringValue :

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), isA( ScoreDoc.class ));

Cette ligne n'est pas appel de la méthode originale getContentStringValue :

doReturn( "dummy" ).when( im ).getContentStringValue( anyInt(), any( ScoreDoc.class ));

Pour des raisons que je ne peux pas répondre, à l'aide de isA() les causes de la destinée (?) "n'appelez pas la méthode" le comportement du doReturn à l'échec.

regardons le signatures de méthode impliquées ici: elles sont toutes les deux static méthodes de Matchers . Tous les deux sont dits par le Javadoc pour retourner null , qui est un peu difficile d'obtenir votre tête autour dans lui-même. Probablement l'objet Class passé comme paramètre est examiné mais le résultat soit jamais calculé ou écarté. Étant donné que null peut représenter n'importe quelle classe et que vous espérez que la méthode moquée ne sera pas appelée, les signatures de isA( ... ) et any( ... ) juste retour null plutôt que d'un paramètre générique* <T> ?

de toute façon:

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 aucun indice à ce sujet. Il semble également dire que la nécessité d'un tel comportement "ne pas appeler la méthode" est "très rare". Personnellement j'utilise cette technique tout le temps : typiquement je trouve que la moquerie implique quelques lignes qui" mettent le décor"... suivie par l'appel d'une méthode qui ensuite "joue" la scène dans le contexte simulé que vous avez mis en scène... et pendant que vous installez le décor et les accessoires, la dernière chose que vous voulez est que les acteurs entrent sur scène à gauche et commencent à jouer leur coeur...

mais c'est bien au-delà de mon niveau de rémunération... J'invite les grands prêtres Mockito à m'expliquer...

* "paramètre générique" le bon terme?

13
répondu mike rodent 2017-03-01 22:07:58

dans mon cas, en utilisant Mockito 2.0, j'ai dû changer tous les paramètres any() En nullable() afin de couper l'appel réel.

10
répondu ejaenv 2017-03-29 20:46:24

j'ai trouvé une autre raison pour que spy appelle la méthode originale.

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 des limites différentes et que nous voulons recueillir l'expérience et les commentaires des utilisateurs, cette fonctionnalité a dû être explicitement activée pour être disponible ; il peut être fait via le mécanisme d'extension mockito par création du fichier src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker contenant une seule ligne: mock-maker-inline

Source: https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#mock-the-unmockable-opt-in-mocking-of-final-classesmethods

après que j'ai fusionné et apporté ce fichier à ma machine, mes tests ont échoué.

j'ai juste eu à enlever la ligne (ou le fichier), et spy() travaillé.

0
répondu Matruskan 2018-01-17 11:57:27

Une façon de s'assurer d'une méthode d'une classe n'est pas appelé à remplacer la méthode avec un mannequin.

    WebFormCreatorActivity activity = spy(new WebFormCreatorActivity(clientFactory) {//spy(new WebFormCreatorActivity(clientFactory));
            @Override
            public void select(TreeItem i) {
                log.debug("SELECT");
            };
        });
0
répondu Geoffrey Ritchey 2018-06-19 21:28:36

un autre scénario possible qui pourrait causer des problèmes avec les espions est quand vous testez Spring beans (avec le cadre de test de ressort) ou quelque autre cadre qui est proxing 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 à la fois le printemps et Mockito va essayer de proxy votre MonitoringDocumentsRepository objet, mais le printemps sera le premier, ce qui causera l'appel réel de findMonitoringDocuments method. Si nous déboguons notre code juste après avoir placé un espion sur un objet de dépôt, il ressemblera à ceci dans le débogueur:

repository = MonitoringDocumentsRepository$$EnhancerBySpringCGLIB$$MockitoMock$

@SpyBean à la rescousse

si à la place @Autowired annotation nous utilisons @SpyBean annotation, nous allons résoudre le problème ci-dessus, l'annotation SpyBean injectera aussi objet de dépôt, mais il sera tout d'abord remplacé par Mockito et ressemblera à ce débogueur intérieur

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));
}
0
répondu Adrian Kapuscinski 2018-10-08 14:10:11