Simuler l'échec du premier appel, le deuxième appel réussit

119

Je souhaite utiliser Mockito pour tester le code (simplifié) ci-dessous. Je ne sais pas comment dire à Mockito d'échouer la première fois, puis de réussir la deuxième fois.

for(int i = 1; i < 3; i++) {
  String ret = myMock.doTheCall();

  if("Success".equals(ret)) {
    log.write("success");
  } else if ( i < 3 ) {
    log.write("failed, but I'll try again. attempt: " + i);
  } else {
    throw new FailedThreeTimesException();
  }
}

Je peux configurer le test de réussite avec:

Mockito.when(myMock).doTheCall().thenReturn("Success");

Et le test d'échec avec:

Mockito.when(myMock).doTheCall().thenReturn("you failed");

Mais comment puis-je tester que si cela échoue une (ou deux fois) puis réussit, tout va bien?

jb.
la source

Réponses:

251

À partir de la documentation :

Parfois, nous devons stub avec une valeur de retour / une exception différente pour le même appel de méthode. Un cas d'utilisation typique pourrait être des itérateurs moqueurs. La version originale de Mockito n'avait pas cette fonctionnalité pour promouvoir la moquerie simple. Par exemple, au lieu d'itérateurs, on pourrait utiliser Iterable ou simplement des collections. Ceux-ci offrent des moyens naturels de stubbing (par exemple en utilisant de vraies collections). Dans de rares scénarios, le stubbing des appels consécutifs peut être utile, cependant:

when(mock.someMethod("some arg"))
   .thenThrow(new RuntimeException())
  .thenReturn("foo");

//First call: throws runtime exception:
mock.someMethod("some arg");

//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));

Donc, dans votre cas, vous voudriez:

when(myMock.doTheCall())
   .thenReturn("You failed")
   .thenReturn("Success");
Jon Skeet
la source
25
Cela m'a orienté dans la bonne direction (merci) pour mon scénario qui traitait des méthodes vides - dans ce cas, vous devez utiliser le style alternatif ...doThrow(new RuntimeException()).doNothing().when(myMock).doTheCall();
haggisandchips
38

Le moyen le plus court d'écrire ce que vous voulez est

when(myMock.doTheCall()).thenReturn("Success", "you failed");

Lorsque vous fournissez plusieurs arguments thenReturncomme ceci, chaque argument sera utilisé au plus une fois, à l'exception du tout dernier argument, qui est utilisé autant de fois que nécessaire. Par exemple, dans ce cas, si vous passez l'appel 4 fois, vous obtiendrez "Succès", "vous avez échoué", "vous avez échoué", "vous avez échoué".

Dawood ibn Kareem
la source
22

Étant donné que le commentaire qui s'y rapporte est difficile à lire, j'ajouterai une réponse formatée.

Si vous essayez de le faire avec une voidfonction qui lève simplement une exception, suivie d'une étape de non-comportement, vous feriez quelque chose comme ceci:

Mockito.doThrow(new Exception("MESSAGE"))
            .doNothing()
            .when(mockService).method(eq());
anoneironaut
la source
4

Pour ajouter à ceci et à cette réponse, vous pouvez également utiliser une boucle pour enchaîner les appels simulés. Ceci est utile si vous devez vous moquer de la même chose plusieurs fois ou vous moquer d'un modèle.

Par exemple (même si c'est tiré par les cheveux):

import org.mockito.stubbing.Stubber;

Stubber stubber = doThrow(new Exception("Exception!"));
for (int i=0; i<10; i++) {
    if (i%2 == 0) {
        stubber.doNothing();
    } else {
        stubber.doThrow(new Exception("Exception"));
    }
}
stubber.when(myMockObject).someMethod(anyString());
faute de frappe
la source
En fait, je ne connaissais pas ce Stubber et je l'ai trouvé utile, +1.
Mihai Morcov
Cela devrait être la bonne réponse si vous avez besoin de la méthode call a void.
db80
4

J'ai une situation différente, je voulais simuler une voidfonction pour le premier appel et l'exécuter normalement au deuxième appel.

Cela fonctionne pour moi:

Mockito.doThrow(new RuntimeException("random runtime exception"))
       .doCallRealMethod()
       .when(spy).someMethod(Mockito.any());
Mohammed Elrashidy
la source