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

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

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.


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 .


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 é :)


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

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


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

1

Estilo BDD:

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.