Usando o Mockito com várias chamadas para o mesmo método com os mesmos argumentos

289

Existe uma maneira de um método stubbed retornar objetos diferentes nas invocações subseqüentes? Eu gostaria de fazer isso para testar respostas não determinadas de um ExecutorCompletionService. isto é, para testar se, independentemente da ordem de retorno dos métodos, o resultado permanece constante.

O código que estou procurando testar é mais ou menos assim.

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

Respostas:

254

Você pode fazer isso usando o thenAnswermétodo (ao encadear com when):

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

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

        return 2;
    }
});

Ou usando o doAnswermétodo estático equivalente :

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

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

        return 2;
    }
}).when(someMock).someMethod();
Igor Nikolaev
fonte
634

E se

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

Você pode colocar quantos argumentos quiser entre colchetes do Return, desde que sejam do tipo correto. O primeiro valor será retornado na primeira vez em que o método for chamado, na segunda resposta e assim por diante. O último valor será retornado repetidamente quando todos os outros valores forem usados.

Dawood ibn Kareem
fonte
4
Isso funcionará com uma farsa, mas não com um espião. Se você precisar impedir a chamada do método original, precisará doAnswer (...). When (someSpy) .someMethod (...).
Yuri
6
@Yuri - não é bem assim. Você não precisa doAnswerou deve escrever um Answerno caso mencionado. Você pode apenas usar doReturn(...).when(someSpy).someMethod(...). Parece razoável supor que Emma esteja interessada em zombarias, em vez de espiões, mas acho que poderia acrescentar algo à minha resposta para explicar isso. Obrigado pelo comentário.
Dawood ibn Kareem
O @DawoodibnKareem, digamos que, na primeira chamada, quero retornar um valor e, na segunda, desejo lançar uma exceção. Como isso pode ser feito?
Rito 11/09
@Rito Por favor, leia a resposta de Volodymyr ou a resposta de Raystorm. Ambos cobrem esse caso.
Dawood ibn Kareem
Uma resposta tão gloriosa.
wild_nothing
151

Como apontado anteriormente, quase todas as chamadas são encadeadas.

Então você poderia ligar

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

Mais informações em Documenation de Mockito .

Raystorm
fonte
3
Muito útil! O que aconteceria na quarta vez que mock.methodfoi chamado neste exemplo? Eu quero algo como, retornar foo pela primeira vez, mas retornar barra para todo o resto.
usar o seguinte código
6
Cada chamada adicional na simulação retornará o último 'thenReturn' ou o último 'thenThrow' Muito útil
Francois Lacoursiere
Obrigado pelas ótimas e simples instruções. Nunca soube disso até agora. Eu estava lutando para descobrir como recuperar dois resultados diferentes em duas chamadas idênticas. Economize muito tempo.
CharlesC
68

Você pode até encadear doReturn()invocações de métodos como esta

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

fofo não é :)

Volodymyr Kozubal
fonte
4

Eu implementei uma MultipleAnswerclasse que me ajuda a esboçar respostas diferentes em cada chamada. Aqui o trecho de código:

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);
    }
}
victorvmp
fonte
1
Você pode inicializar esse objeto de maneira curta, simples e legível?
usr-local-ΕΨΗΕΛΩΝ
1

A seguir, pode ser usado como um método comum para retornar argumentos diferentes em diferentes chamadas de método. A única coisa que precisamos fazer é passar uma matriz com a ordem na qual os objetos devem ser recuperados em cada chamada.

@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);retornará o objeto mock1 na primeira chamada, o objeto mock3 na segunda chamada e o objeto mock2 na terceira chamada. Deve ser usado como when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); Este é quase semelhante aowhen(something()).thenReturn(mock1, mock3, mock2);

yuva 443
fonte
1

Relacionadas com @ [Igor Nikolaev] resposta 's a partir de 8 anos atrás, usando um Answerpode ser simplificado um pouco usando uma expressão lambda disponível em Java 8.

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

ou mais simplesmente:

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

Estilo BDD:

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

doReturn (valor1, valor2, valor3) .when (chamada de método)

EnhancedJack
fonte