Mockito: responder versus retornar

124

Estou usando o Mockito para teste de unidade posterior do serviço. Estou confuso quando usar doAnswervs thenReturn.

Alguém pode me ajudar em detalhes? Até agora, tentei com thenReturn.

Rajkumar Thambu
fonte

Respostas:

167

Você deve usar thenReturnou doReturnquando souber o valor de retorno no momento em que simular uma chamada de método. Este valor definido é retornado quando você invoca o método simulado.

thenReturn(T value) Define um valor de retorno a ser retornado quando o método é chamado.

@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 é usado quando você precisa realizar ações adicionais quando um método simulado é invocado, por exemplo, quando você precisa calcular o valor de retorno com base nos parâmetros desta chamada de método.

Use doAnswer()quando quiser criar um stub de método void com genérico Answer.

A resposta especifica uma ação que é executada e um valor de retorno que é retornado quando você interage com a simulação.

@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
fonte
oi @Roland Weisleder mas às vezes você deve retornar algum código interno gerado por valor e nada a ver com argumentos, por exemplo code = UUID.randomUUID(), achei impossível implementar isso mockito.
zhuguowei
3
Quando seu mock deve retornar um novo UUID para cada invocação, você implementaria Answerapenas com return UUID.randomUUID();.
Roland Weisleder
Posso pegar esse método da nova inicialização do Answer e colocá-lo em algum método, para deixar o código um pouco mais limpo?
Linha
3
@Line Answeré uma interface funcional, então com Java 8 você pode substituí-la por uma expressão lambda. Se o não estiver limpo o suficiente, qualquer outra refatoração usual e incomum é possível.
Roland Weisleder
@ zhuguowei: retornar algum código interno gerado por valor? O que você quer dizer com isso?
Saurabh Patil
34

doAnswere thenReturnfaça a mesma coisa se:

  1. Você está usando o Mock, não o Spy
  2. O método que você está fazendo o stub está retornando um valor, não um método void.

Vamos zombar deste BookService

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

Você pode criar um 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();

Observe que, ao usar doAnswer, você não pode passar um método adiante when.

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

Então, quando você usaria em doAnswervez de thenReturn? Posso pensar em dois casos de uso:

  1. Quando você deseja "stub" o método void.

Usando doAnswer, você pode realizar algumas ações adicionais na chamada do método. Por exemplo, acione um retorno de chamada em 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 você está usando o Spy em vez do Mock

Ao usar when-thenReturn on, o Spy Mockito chamará o método real e, em seguida, esboçará sua resposta. Isso pode causar um problema se você não quiser chamar o método real, como neste exemplo:

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 criar um stub com segurança.

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

Na verdade, se você não quiser fazer ações adicionais na invocação do método, você pode apenas usar doReturn.

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
Aldok
fonte
e se o método simulado for nulo?
Igor Donin
1
Igor, é exatamente aí que doAnswer () entra em cena e ele abordou isso na resposta acima.
Saurabh Patil
Ao usar, doAnswer(new Answer() { ... return null;}recebo um aviso no eclipse para "A resposta é um tipo bruto. As referências ao tipo genérico Resposta <T> devem ser parametrizadas". Existe uma maneira de resolver isso (exceto ignorar o aviso ofc)?
LazR de