Używanie Mockito z wieloma wywołaniami do tej samej metody z tymi samymi argumentami

289

Czy istnieje sposób, aby metoda skrótowa zwracała różne obiekty przy kolejnych wywołaniach? Chciałbym to zrobić, aby przetestować nieokreślone odpowiedzi od ExecutorCompletionService. tj. w celu przetestowania, niezależnie od kolejności zwrotu metod, wynik pozostaje stały.

Kod, który chcę przetestować, wygląda mniej więcej tak.

// 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
źródło

Odpowiedzi:

254

Możesz to zrobić przy użyciu thenAnswermetody (podczas łączenia z when):

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

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

        return 2;
    }
});

Lub przy użyciu równoważnej doAnswermetody statycznej :

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

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

        return 2;
    }
}).when(someMock).someMethod();
Igor Nikołajew
źródło
635

Co powiesz na

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

Możesz wstawić dowolną liczbę argumentów w nawiasach następnie Return, pod warunkiem, że wszystkie są poprawne. Pierwsza wartość zostanie zwrócona przy pierwszym wywołaniu metody, następnie druga odpowiedź i tak dalej. Ostatnia wartość zostanie zwrócona wielokrotnie, gdy wszystkie pozostałe wartości zostaną wykorzystane.

Dawood ibn Kareem
źródło
4
Będzie to działać z próbą, ale nie ze szpiegiem. Jeśli chcesz zapobiec wywołaniu oryginalnej metody, potrzebujesz doAnswer (...). When (someSpy) .someMethod (...).
Yuri,
6
@Yuri - niezupełnie. Nie musisz doAnswerpisać ani pisać Answerw sprawie, o której wspominasz. Możesz po prostu użyć doReturn(...).when(someSpy).someMethod(...). Rozsądne wydaje się założenie, że Emma interesuje się szyderstwami, a nie szpiegami, ale chyba mógłbym dodać coś do mojej odpowiedzi, aby to przeliterować. Dziękuję za komentarz.
Dawood ibn Kareem
@DawoodibnKareem pozwala powiedzieć, że dla pierwszego połączenia chcę zwrócić wartość, a dla drugiego połączenia chcę zgłosić wyjątek. Jak można to zrobić?
Rito,
@ Rito Proszę przeczytać odpowiedź Wołodymyra lub odpowiedź Raystorma. Oba obejmują tę sprawę.
Dawood ibn Kareem,
Taka chwalebna odpowiedź.
wild_nothing
151

Jak już wspomniano, prawie wszystkie połączenia można łączyć w łańcuchy.

Więc możesz zadzwonić

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

Więcej informacji w Dokumentacji Mockito .

Raystorm
źródło
3
Bardzo pomocne! Co stałoby się po raz czwarty mock.methodw tym przykładzie? Chcę coś w stylu, wróć za pierwszym razem, ale wróć do wszystkich pozostałych.
javaPlease42
6
Każde dodatkowe wywołanie na makiecie zwróci ostatnie „następnie powrót” lub ostatnie „następnie rzut” Bardzo przydatne
Francois Lacoursiere
Dziękuję za wspaniałe i proste instrukcje. Nigdy o tym nie wiedziałem. Z trudem znajdowałem, jak odzyskać dwa różne wyniki przy dwóch identycznych połączeniach. Zaoszczędź mi mnóstwo czasu.
CharlesC
68

Możesz nawet łączyć doReturn()takie wywołania metod

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

słodkie, prawda?

Włodzimierz Kozubal
źródło
4

Wdrożyłem MultipleAnswerklasę, która pomaga mi odgadnąć różne odpowiedzi w każdym połączeniu. Oto fragment kodu:

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
źródło
1
Czy możesz zainicjować ten obiekt w krótki, prosty i czytelny sposób?
usr-local-ΕΨΗΕΛΩΝ
1

Poniższe mogą być używane jako wspólna metoda zwracania różnych argumentów dla różnych wywołań metod. Jedyne, co musimy zrobić, to przekazać tablicę w kolejności, w której obiekty powinny być pobierane w każdym wywołaniu.

@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;    
       } 
    }
}

Dawny. getAnswerForSubsequentCalls(mock1, mock3, mock2);zwróci obiekt mock1 przy pierwszym wywołaniu, obiekt mock3 przy drugim wywołaniu i obiekt mock2 przy trzecim wywołaniu. Powinny być używane jak when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); To jest prawie podobne dowhen(something()).thenReturn(mock1, mock3, mock2);

yuva 443
źródło
1

Powiązane z odpowiedzią @ [Igora Nikołajewa] sprzed 8 lat, Answermożna nieco uprościć użycie wyrażenia lambda dostępnego w Javie 8.

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

lub prościej:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());
MorganGalpin
źródło
1

Styl BDD:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);
epoksydowy
źródło
1

doReturn (wartość1, wartość2, wartość3) .when (wywołanie metody)

ulepszoneJack
źródło