Der erste Anruf simuliert schlägt fehl, der zweite Anruf ist erfolgreich

119

Ich möchte Mockito verwenden, um den (vereinfachten) Code unten zu testen. Ich weiß nicht, wie ich Mockito sagen soll, dass er beim ersten Mal scheitern und beim zweiten Mal erfolgreich sein soll.

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();
  }
}

Ich kann den Erfolgstest einrichten mit:

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

Und der Fehlertest mit:

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

Aber wie kann ich testen, ob es erfolgreich ist, wenn es einmal (oder zweimal) fehlschlägt?

jb.
quelle

Antworten:

250

Aus den Dokumenten :

Manchmal müssen wir für denselben Methodenaufruf mit unterschiedlichen Rückgabewerten / Ausnahmen stubben. Ein typischer Anwendungsfall könnte das Verspotten von Iteratoren sein. Die Originalversion von Mockito hatte diese Funktion nicht, um einfaches Verspotten zu fördern. Beispielsweise könnte man anstelle von Iteratoren Iterable oder einfach Sammlungen verwenden. Diese bieten natürliche Möglichkeiten zum Stubben (z. B. unter Verwendung realer Sammlungen). In seltenen Fällen kann es jedoch nützlich sein, aufeinanderfolgende Anrufe zu stoppen:

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"));

In Ihrem Fall möchten Sie also:

when(myMock.doTheCall())
   .thenReturn("You failed")
   .thenReturn("Success");
Jon Skeet
quelle
25
Dies wies mich in die richtige Richtung (danke) für mein Szenario, das sich mit ungültigen Methoden befasste - in diesem Fall müssen Sie den alternativen Stil verwenden ...doThrow(new RuntimeException()).doNothing().when(myMock).doTheCall();
haggisandchips
38

Der kürzeste Weg zu schreiben, was Sie wollen, ist

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

Wenn Sie mehrere Argumente angeben, um thenReturndies zu mögen, wird jedes Argument höchstens einmal verwendet, mit Ausnahme des allerletzten Arguments, das so oft wie nötig verwendet wird. Wenn Sie in diesem Fall beispielsweise viermal telefonieren, erhalten Sie "Erfolg", "Sie haben versagt", "Sie haben versagt", "Sie haben versagt".

Dawood ibn Kareem
quelle
22

Da der diesbezügliche Kommentar schwer zu lesen ist, füge ich eine formatierte Antwort hinzu.

Wenn Sie dies mit einer voidFunktion versuchen, die nur eine Ausnahme auslöst, gefolgt von einem Schritt ohne Verhalten, würden Sie Folgendes tun:

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

Um diese und diese Antwort zu ergänzen , können Sie auch eine Schleife verwenden, um die verspotteten Anrufe zu verketten. Dies ist nützlich, wenn Sie dasselbe mehrmals oder in einem bestimmten Muster verspotten müssen.

ZB (wenn auch weit hergeholt):

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());
typoerrpr
quelle
Ich wusste eigentlich nichts über diesen Stubber und fand ihn nützlich, +1.
Mihai Morcov
Dies sollte die richtige Antwort sein, wenn Sie den Aufruf einer void-Methode benötigen.
db80
4

Ich habe eine andere Situation, ich wollte eine voidFunktion für den ersten Aufruf verspotten und sie beim zweiten Aufruf normal ausführen.

Das funktioniert bei mir:

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