Mockito: doAnswer Vs thenReturn

124

Estoy usando Mockito para pruebas unitarias posteriores del servicio. Estoy confundido cuándo utilizar doAnswervs thenReturn.

¿Alguien puede ayudarme en detalle? Hasta ahora, lo he probado con thenReturn.

Rajkumar Thambu
fuente

Respuestas:

167

Debe usar thenReturno doReturncuando conozca el valor de retorno en el momento en que simula una llamada al método. Este valor definido se devuelve cuando invoca el método simulado.

thenReturn(T value) Establece un valor de retorno que se devolverá cuando se llame al método.

@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 se utiliza cuando necesita realizar acciones adicionales cuando se invoca un método simulado, por ejemplo, cuando necesita calcular el valor de retorno basado en los parámetros de esta llamada al método.

Úselo doAnswer()cuando desee apuntar un método vacío con genérico Answer.

Respuesta especifica una acción que se ejecuta y un valor de retorno que se devuelve cuando interactúa con el simulacro.

@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");
}
Roland Weisleder
fuente
hola @Roland Weisleder, pero a veces debería devolver algún código interno generado por valor y nada que ver con argumentos, por ejemplo code = UUID.randomUUID(), encontré imposible implementar esto mockito.
zhuguowei
3
Cuando su simulacro debería devolver un nuevo UUID para cada invocación, implementaría el Answerjust con return UUID.randomUUID();.
Roland Weisleder
¿Puedo tomar este método de la nueva inicialización de Respuesta y ponerlo en algún método, para hacer el código un poco más limpio?
Línea
3
@Line Answeres una interfaz funcional, por lo que con Java 8 podría reemplazarla con una expresión lambda. Si no está lo suficientemente limpio, es posible realizar otra refactorización habitual e inusual.
Roland Weisleder
@ zhuguowei: ¿devolver algún valor de código interno generado? ¿Qué quieres decir con eso?
Saurabh Patil
34

doAnswery thenReturnhaz lo mismo si:

  1. Estás usando Mock, no Spy
  2. El método que está aplicando está devolviendo un valor, no un método vacío.

Vamos a burlarnos de este BookService

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

Puede stub getAuthor () usando doAnswery 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();

Tenga en cuenta que cuando lo usa doAnswer, no puede pasar un método when.

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

Entonces, ¿cuándo usarías en doAnswerlugar de thenReturn? Puedo pensar en dos casos de uso:

  1. Cuando desee "anular" el método.

Con doAnswer puede realizar algunas acciones adicionales tras la invocación del método. Por ejemplo, active una devolución de llamada en 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. Cuando usa Spy en lugar de Mock

Cuando use when-thenReturn on Spy, Mockito llamará al método real y luego eliminará su respuesta. Esto puede causar un problema si no desea llamar al método real, como en este ejemplo:

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 podemos eliminarlo de forma segura.

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

En realidad, si no desea realizar acciones adicionales tras la invocación del método, puede usar doReturn.

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
aldok
fuente
¿Qué pasa si el método simulado es nulo?
Igor Donin
1
Igor, ahí es exactamente donde entra en escena doAnswer () y lo ha cubierto en la respuesta anterior.
Saurabh Patil
Cuando lo uso, doAnswer(new Answer() { ... return null;}aparece una advertencia en eclipse para "La respuesta es un tipo sin formato. Las referencias al tipo genérico Respuesta <T> deben parametrizarse". ¿Hay alguna forma de resolver esto (excepto ignorar la advertencia de c)?
LazR