Verwenden von Mockito mit mehreren Aufrufen derselben Methode mit denselben Argumenten

289

Gibt es eine Möglichkeit, dass eine Stubbed-Methode bei nachfolgenden Aufrufen unterschiedliche Objekte zurückgibt? Ich möchte dies tun, um unbestimmte Antworten von einem zu testen ExecutorCompletionService. dh um zu testen, dass unabhängig von der Rückgabereihenfolge der Methoden das Ergebnis konstant bleibt.

Der Code, den ich testen möchte, sieht ungefähr so ​​aus.

// Create an completion service so we can group these tasks together
ExecutorCompletionService<T> completionService =
        new ExecutorCompletionService<T>(service);

// Add all these tasks to the completion service
for (Callable<T> t : ts)
    completionService.submit(request);

// As an when each call finished, add it to the response set.
for (int i = 0; i < calls.size(); i ++) {
    try {
        T t = completionService.take().get();
        // do some stuff that I want to test
    } catch (...) { }        
}
Emma
quelle

Antworten:

254

Sie können dies mit der folgenden thenAnswerMethode tun (beim Verketten mit when):

when(someMock.someMethod()).thenAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
});

Oder verwenden Sie die entsprechende statische doAnswerMethode:

doAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
}).when(someMock).someMethod();
Igor Nikolaev
quelle
634

Wie wäre es mit

when( method-call ).thenReturn( value1, value2, value3 );

Sie können so viele Argumente in die Klammern von thenReturn setzen, wie Sie möchten, vorausgesetzt, sie haben alle den richtigen Typ. Der erste Wert wird beim ersten Aufruf der Methode zurückgegeben, dann die zweite Antwort usw. Der letzte Wert wird wiederholt zurückgegeben, sobald alle anderen Werte aufgebraucht sind.

Dawood ibn Kareem
quelle
4
Dies funktioniert mit einem Schein, aber nicht mit einem Spion. Wenn Sie verhindern möchten, dass die ursprüngliche Methode aufgerufen wird, benötigen Sie doAnswer (...). When (someSpy) .someMethod (...).
Yuri
6
@ Yuri - nicht ganz. Sie brauchen doAnsweroder schreiben keine Answerin dem Fall, den Sie erwähnen. Sie können einfach verwenden doReturn(...).when(someSpy).someMethod(...). Es scheint vernünftig anzunehmen, dass Emma eher an Spott als an Spionen interessiert ist, aber ich denke, ich könnte meiner Antwort etwas hinzufügen, um dies zu formulieren. Danke für den Kommentar.
Dawood ibn Kareem
@DawoodibnKareem sagt, für den ersten Aufruf möchte ich einen Wert zurückgeben und für den zweiten Aufruf möchte ich eine Ausnahme auslösen. Wie kann das gemacht werden?
Rito
@Rito Bitte lesen Sie die Antwort von Volodymyr oder Raystorm. Sie decken beide diesen Fall ab.
Dawood ibn Kareem
So eine herrliche Antwort.
wild_nothing
151

Wie bereits erwähnt, sind fast alle Anrufe verkettbar.

Sie könnten also anrufen

when(mock.method()).thenReturn(foo).thenReturn(bar).thenThrow(new Exception("test"));

//OR if you're mocking a void method and/or using spy instead of mock

doReturn(foo).doReturn(bar).doThrow(new Exception("Test").when(mock).method();

Weitere Informationen in Mockitos Dokumentation .

Raystorm
quelle
3
Sehr hilfreich! Was würde passieren, wenn das 4. Mal mock.methodin diesem Beispiel aufgerufen wurde? Ich möchte so etwas wie, beim ersten Mal foo zurückkehren, aber für den Rest den Balken zurückgeben.
JavaPlease42
6
Jeder zusätzliche Aufruf auf dem Mock gibt das letzte 'thenReturn' oder das letzte 'thenThrow' zurück. Sehr nützlich
Francois Lacoursiere
Vielen Dank für die tolle und einfache Anleitung. Ich habe das bis jetzt nie gewusst. Ich hatte Mühe herauszufinden, wie ich bei zwei identischen Anrufen zwei unterschiedliche Ergebnisse zurückerhalten konnte. Sparen Sie mir jede Menge Zeit.
CharlesC
68

Sie können sogar doReturn()Methodenaufrufe wie diese verketten

doReturn(null).doReturn(anotherInstance).when(mock).method();

süß ist es nicht :)

Volodymyr Kozubal
quelle
4

Ich habe eine MultipleAnswerKlasse implementiert , die mir hilft, bei jedem Anruf unterschiedliche Antworten zu finden. Hier der Code:

private final class MultipleAnswer<T> implements Answer<T> {

    private final ArrayList<Answer<T>> mAnswers;

    MultipleAnswer(Answer<T>... answer) {
        mAnswers = new ArrayList<>();
        mAnswers.addAll(Arrays.asList(answer));
    }

    @Override
    public T answer(InvocationOnMock invocation) throws Throwable {
        return mAnswers.remove(0).answer(invocation);
    }
}
victorvmp
quelle
1
Können Sie dieses Objekt auf kurze, einfache und lesbare Weise initialisieren?
usr-local-ΕΨΗΕΛΩΝ
1

Das Folgende kann als gemeinsame Methode verwendet werden, um verschiedene Argumente bei verschiedenen Methodenaufrufen zurückzugeben. Wir müssen nur ein Array mit der Reihenfolge übergeben, in der Objekte bei jedem Aufruf abgerufen werden sollen.

@SafeVarargs
public static <Mock> Answer<Mock> getAnswerForSubsequentCalls(final Mock... mockArr) {
    return new Answer<Mock>() {
       private int count=0, size=mockArr.length;
       public Mock answer(InvocationOnMock invocation) throws throwable {
           Mock mock = null;
           for(; count<size && mock==null; count++){
                mock = mockArr[count];
           }

           return mock;    
       } 
    }
}

Ex. getAnswerForSubsequentCalls(mock1, mock3, mock2);gibt beim ersten Aufruf das Objekt mock1, beim zweiten Aufruf das Objekt mock3 und beim dritten Aufruf das Objekt mock2 zurück. Sollte wie when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); folgt verwendet werden. Dies ist fast ähnlich zuwhen(something()).thenReturn(mock1, mock3, mock2);

Yuva 443
quelle
1

Im Zusammenhang mit der Antwort von @ [Igor Nikolaev] vor 8 Jahren Answerkann die Verwendung von a mithilfe eines in Java 8 verfügbaren Lambda-Ausdrucks etwas vereinfacht werden .

when(someMock.someMethod()).thenAnswer(invocation -> {
    doStuff();
    return;
});

oder einfacher:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());
MorganGalpin
quelle
1

BDD-Stil:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);
Epox
quelle
1

doReturn (Wert1, Wert2, Wert3) .when (Methodenaufruf)

EnhancedJack
quelle