Mockito: doAnswer Vs thenReturn


124

Sto usando Mockito per il servizio successivo di unit test. Sono confuso quando utilizzare doAnswervs thenReturn.

Qualcuno può aiutarmi in dettaglio? Finora l'ho provato con thenReturn.

Risposte:


167

Dovresti usare thenReturno doReturnquando conosci il valore restituito nel momento in cui deridi una chiamata al metodo. Questo valore definito viene restituito quando si richiama il metodo mocked.

thenReturn(T value) Imposta un valore restituito da restituire quando viene chiamato il metodo.

@Test
public void test_return() throws Exception {
    Dummy dummy = mock(Dummy.class);
    int returnValue = 5;

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenReturn(returnValue);
    doReturn(returnValue).when(dummy).stringLength("dummy");
}

Answer viene utilizzato quando è necessario eseguire azioni aggiuntive quando viene richiamato un metodo deriso, ad esempio quando è necessario calcolare il valore restituito in base ai parametri di questa chiamata al metodo.

Da utilizzare doAnswer()quando si desidera bloccare un metodo void con generic Answer.

La risposta specifica un'azione che viene eseguita e un valore restituito che viene restituito quando si interagisce con il mock.

@Test
public void test_answer() throws Exception {
    Dummy dummy = mock(Dummy.class);
    Answer<Integer> answer = new Answer<Integer>() {
        public Integer answer(InvocationOnMock invocation) throws Throwable {
            String string = invocation.getArgumentAt(0, String.class);
            return string.length() * 2;
        }
    };

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenAnswer(answer);
    doAnswer(answer).when(dummy).stringLength("dummy");
}

ciao @Roland Weisleder ma a volte dovresti restituire un codice interno generato da un valore e niente a che fare con gli argomenti, ad esempio code = UUID.randomUUID(), ho trovato impossibile implementarlo con mockito.
Zhuguowei

3
Quando il tuo mock dovrebbe restituire un nuovo UUID per ogni invocazione, implementeresti il Answerjust with return UUID.randomUUID();.
Roland Weisleder

Posso prendere questo metodo dalla nuova inizializzazione della risposta e inserirlo in qualche metodo, per rendere il codice un po 'più pulito?
Linea

3
@Line Answerè un'interfaccia funzionale, quindi con Java 8 potresti sostituirla con un'espressione lambda. Se il non è abbastanza pulito, è possibile qualsiasi altro refactoring usuale e insolito.
Roland Weisleder

@ zhuguowei: restituire un codice interno generato dal valore? Cosa vuoi dire con questo?
Saurabh Patil

34

doAnswere thenReturnfai la stessa cosa se:

  1. Stai usando Mock, non Spy
  2. Il metodo che stai stubbing restituisce un valore, non un metodo void.

Prendiamo in giro questo BookService

public interface BookService {
    String getAuthor();
    void queryBookTitle(BookServiceCallback callback);
}

Puoi stub getAuthor () usando doAnswere thenReturn.

BookService service = mock(BookService.class);
when(service.getAuthor()).thenReturn("Joshua");
// or..
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return "Joshua";
    }
}).when(service).getAuthor();

Nota che quando usi doAnswer, non puoi trasmettere un metodo when.

// Will throw UnfinishedStubbingException
doAnswer(invocation -> "Joshua").when(service.getAuthor());

Quindi, quando useresti doAnswerinvece di thenReturn? Mi vengono in mente due casi d'uso:

  1. Quando si desidera "stub" metodo void.

Utilizzando doAnswer è possibile eseguire alcune azioni aggiuntive durante l'invocazione del metodo. Ad esempio, attiva una richiamata su queryBookTitle.

BookServiceCallback callback = new BookServiceCallback() {
    @Override
    public void onSuccess(String bookTitle) {
        assertEquals("Effective Java", bookTitle);
    }
};
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        BookServiceCallback callback = (BookServiceCallback) invocation.getArguments()[0];
        callback.onSuccess("Effective Java");
        // return null because queryBookTitle is void
        return null;
    }
}).when(service).queryBookTitle(callback);
service.queryBookTitle(callback);
  1. Quando usi Spy invece di Mock

Quando si utilizza quando-allora, il ritorno su Spy Mockito chiamerà il metodo reale e quindi bloccherà la tua risposta. Ciò può causare un problema se non si desidera chiamare il metodo reale, come in questo esempio:

List list = new LinkedList();
List spy = spy(list);
// Will throw java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
when(spy.get(0)).thenReturn("java");
assertEquals("java", spy.get(0));

Usando doAnswer possiamo bloccarlo in sicurezza.

List list = new LinkedList();
List spy = spy(list);
doAnswer(invocation -> "java").when(spy).get(0);
assertEquals("java", spy.get(0));

In realtà, se non vuoi eseguire azioni aggiuntive durante l'invocazione del metodo, puoi semplicemente usare doReturn.

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));

e se il metodo deriso fosse nullo?
Igor Donin

1
Igor, questo è esattamente il punto in cui doAnswer () entra in scena e lo ha spiegato nella risposta sopra.
Saurabh Patil

Quando si utilizza doAnswer(new Answer() { ... return null;}ottengo un avviso in Eclipse per "La risposta è di tipo grezzo. I riferimenti al tipo generico Risposta <T> devono essere parametrizzati". C'è un modo per risolvere questo problema (tranne ignorare l'avvertimento di c)?
LazR
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.