2011-11-11 15 views
153

C'è un modo per avere un metodo stub restituito oggetti diversi nelle chiamate successive? Mi piacerebbe farlo per testare risposte non determinabili da un ExecutorCompletionService. per testare che indipendentemente dall'ordine di restituzione dei metodi, il risultato rimane costante.Utilizzo di Mockito con più chiamate allo stesso metodo con gli stessi argomenti

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

risposta

140

Potete farlo utilizzando il metodo thenAnswer (quando 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 utilizzando l'equivalente, statica doAnswer metodo:

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

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

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

oooh, pulito. i documenti parlano di Answer come solo per vuoto, ma questo è fantastico! – Emma

+1

Questo in realtà aiuta molto quando si sopprimono metodi void che devono fornire risposte ai callback, in cui non è possibile utilizzare un 'thenReturn (val1, val2, ...)' più semplice. Grazie molto! –

+1

Come un'altra opzione per la risposta di cui sopra, è possibile anche concatenare insieme metodi DoAnswer. Quindi '(someMock.someMethod()). DoAnswer (new Answer1()). DoAnswer (new Answer2());' –

400

Come su

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

Puoi mettere tutti gli argomenti che vuoi nelle parentesi di thenReturn, sempre che siano 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.

+2

Questo funzionerà con una simulazione, ma non con una spia. Se è necessario evitare di chiamare il metodo originale è necessario doAnswer (...). When (someSpy) .someMethod (...). – Yuri

+1

@Yuri - non del tutto. Non hai bisogno di 'doAnswer' o di scrivere una' Answer' nel caso che tu menzioni. Puoi semplicemente usare 'doReturn (...). When (someSpy) .someMethod (...)'. Sembra ragionevole presumere che Emma sia interessata ai matti, piuttosto che alle spie, ma credo che potrei aggiungere qualcosa alla mia risposta per spiegarlo. Grazie per il commento. –

+0

@DawoodibnKareem consente di dire per la prima chiamata che desidero restituire un valore e per la seconda chiamata voglio generare un'eccezione. Come si può fare? – Rito

50

si può anche catena doReturn() chiamate di metodi come questo

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

carino non è vero :)

54

Come previously pointed out quasi tutte le chiamate sono chainable.

Così si potrebbe 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(); 

More info in Mockito's Documenation.

+2

Molto utile! Cosa accadrebbe la quarta volta in cui è stato chiamato 'mock.method' in questo esempio? Voglio qualcosa del tipo, return foo la prima volta ma return bar per TUTTO il resto. – javaPlease42

+3

Ogni invocazione aggiuntiva sul mock restituirà l'ultimo 'thenReturn' o l'ultimo 'thenThrow' Molto utile –

0

Di seguito è possibile utilizzare come metodo comune per restituire argomenti diversi su chiamate di metodo differenti. L'unica cosa che dobbiamo fare è passare un array con l'ordine in cui gli oggetti devono 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;  
     } 
    } 
} 

Es. 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 utilizzato come when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); Questo è quasi simile a when(something()).thenReturn(mock1, mock3, mock2);

4

Ho implementato una classe MultipleAnswer che mi aiuta a stub risposte diverse a ogni chiamata. Qui 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); 
    } 
} 
Problemi correlati