Utilizzo di Mockito con più chiamate allo stesso metodo con gli stessi argomenti


289

C'è un modo per fare in modo che un metodo stubbing restituisca oggetti diversi nelle invocazioni successive? Mi piacerebbe fare questo per testare risposte non determinate da un ExecutorCompletionService. cioè per verificare che indipendentemente dall'ordine di ritorno dei metodi, il risultato rimane costante.

Il codice che sto cercando di testare assomiglia a questo.

// 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 (...) { }        
}

Risposte:


254

Puoi farlo usando il thenAnswermetodo (durante il concatenamento con when):

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

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

        return 2;
    }
});

O usando il doAnswermetodo statico equivalente :

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

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

        return 2;
    }
}).when(someMock).someMethod();

634

Che ne dite di

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

Puoi inserire tutti gli argomenti che desideri tra parentesi di Ritorno, a condizione che siano tutti del tipo corretto. Il primo valore verrà restituito la prima volta che viene chiamato il metodo, quindi la seconda risposta e così via. L'ultimo valore verrà restituito ripetutamente una volta esauriti tutti gli altri valori.


4
Funzionerà con una simulazione, ma non con una spia. Se è necessario impedire di chiamare il metodo originale, è necessario doAnswer (...). When (someSpy) .someMethod (...).
Yuri,

6
@Yuri - non proprio. Non è necessario doAnswero scrivere Answernel caso in cui si menziona. Puoi solo usare doReturn(...).when(someSpy).someMethod(...). Sembra ragionevole supporre che Emma sia interessata alle beffe, piuttosto che alle spie, ma immagino che potrei aggiungere qualcosa alla mia risposta per spiegarlo. Grazie per il commento.
Dawood ibn Kareem,

@DawoodibnKareem diciamo per la prima chiamata che voglio restituire un valore e per la seconda chiamata voglio lanciare un'eccezione. Come si può fare?
Rito,

@Rito Per favore leggi la risposta di Volodymyr o la risposta di Raystorm. Entrambi coprono quel caso.
Dawood ibn Kareem,

Una risposta così gloriosa.
wild_nothing

151

Come precedentemente sottolineato, quasi tutte le chiamate sono concatenabili.

Quindi potresti chiamare

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

Maggiori informazioni nella documentazione di Mockito .


3
Molto utile! Cosa sarebbe successo la quarta volta è mock.methodstata chiamata in questo esempio? Voglio qualcosa del genere, torna indietro la prima volta, ma barra di ritorno per TUTTO il resto.
javaPlease42

6
Ogni invocazione aggiuntiva sul mock restituirà l'ultimo 'poi Ritorno' o l'ultimo 'poi Lanciare' Molto utile
Francois Lacoursiere,

Grazie per le grandi e semplici istruzioni. Non l'ho mai saputo fino ad ora. Stavo lottando per trovare come ottenere due risultati diversi su due chiamate identiche. Risparmiami un sacco di tempo.
CharlesC

68

Puoi persino concatenare doReturn()invocazioni di metodi come questo

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

carino non è vero :)


4

Ho implementato una MultipleAnswerclasse che mi aiuta a stub risposte diverse in ogni chiamata. Ecco il pezzo di codice:

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

1
Puoi inizializzare quell'oggetto in modo breve, semplice e leggibile?
usr-local-ΕΨΗΕΛΩΝ

1

Di seguito può essere utilizzato come metodo comune per restituire argomenti diversi su chiamate a metodi diversi. L'unica cosa che dobbiamo fare è che dobbiamo passare un array con l'ordine in cui gli oggetti dovrebbero essere recuperati in ogni chiamata.

@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);restituirà l'oggetto mock1 alla prima chiamata, l'oggetto mock3 alla seconda chiamata e l'oggetto mock2 alla terza chiamata. Dovrebbe essere usato come when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); Questo è quasi simile awhen(something()).thenReturn(mock1, mock3, mock2);


1

Relativo alla risposta di [[Igor Nikolaev] di 8 anni fa, l'uso di un Answerpuò essere in qualche modo semplificato usando un'espressione lambda disponibile in Java 8.

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

o più semplicemente:

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

1

Stile BDD:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.