Simular primeira chamada falha, segunda chamada bem-sucedida

119

Quero usar o Mockito para testar o código (simplificado) abaixo. Não sei como dizer a Mockito para fracassar na primeira vez e depois ter sucesso na segunda vez.

for(int i = 1; i < 3; i++) {
  String ret = myMock.doTheCall();

  if("Success".equals(ret)) {
    log.write("success");
  } else if ( i < 3 ) {
    log.write("failed, but I'll try again. attempt: " + i);
  } else {
    throw new FailedThreeTimesException();
  }
}

Posso configurar o teste de sucesso com:

Mockito.when(myMock).doTheCall().thenReturn("Success");

E o teste de falha com:

Mockito.when(myMock).doTheCall().thenReturn("you failed");

Mas como posso testar se, se falhar uma (ou duas vezes), está tudo bem?

jb.
fonte

Respostas:

250

Dos documentos :

Às vezes, precisamos fazer um stub com valor de retorno / exceção diferente para a mesma chamada de método. Caso de uso típico pode ser iteradores de simulação A versão original do Mockito não tinha esse recurso para promover uma simulação simples. Por exemplo, em vez de iteradores, pode-se usar Iterable ou simplesmente coleções. Esses oferecem formas naturais de stubbing (por exemplo, usando coleções reais). Em cenários raros, fazer o stub de chamadas consecutivas pode ser útil:

when(mock.someMethod("some arg"))
   .thenThrow(new RuntimeException())
  .thenReturn("foo");

//First call: throws runtime exception:
mock.someMethod("some arg");

//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));

Então, no seu caso, você deseja:

when(myMock.doTheCall())
   .thenReturn("You failed")
   .thenReturn("Success");
Jon Skeet
fonte
25
Isso me apontou na direção certa (obrigado) para o meu cenário que estava lidando com métodos vazios - nesse caso, você precisa usar o estilo alternativo ...doThrow(new RuntimeException()).doNothing().when(myMock).doTheCall();
haggisandchips
38

A maneira mais curta de escrever o que você quer é

when(myMock.doTheCall()).thenReturn("Success", "you failed");

Quando você fornece vários argumentos para thenReturnassim, cada argumento será usado no máximo uma vez, exceto para o último argumento, que é usado quantas vezes forem necessárias. Por exemplo, neste caso, se você fizer a ligação 4 vezes, obterá "Sucesso", "você falhou", "falhou", "falhou".

Dawood ibn Kareem
fonte
22

Como o comentário relacionado a isso é difícil de ler, adicionarei uma resposta formatada.

Se você estiver tentando fazer isso com uma voidfunção que apenas lança uma exceção, seguida por uma etapa sem comportamento, você faria algo assim:

Mockito.doThrow(new Exception("MESSAGE"))
            .doNothing()
            .when(mockService).method(eq());
anoneironauta
fonte
4

Para adicionar a isso e a isso resposta, você também pode usar um loop para encadear as chamadas simuladas. Isso é útil se você precisa simular a mesma coisa várias vezes, ou simular em algum padrão.

Por exemplo, (embora improvável):

import org.mockito.stubbing.Stubber;

Stubber stubber = doThrow(new Exception("Exception!"));
for (int i=0; i<10; i++) {
    if (i%2 == 0) {
        stubber.doNothing();
    } else {
        stubber.doThrow(new Exception("Exception"));
    }
}
stubber.when(myMockObject).someMethod(anyString());
typoerrpr
fonte
Na verdade, eu não sabia sobre este Stubber e achei útil, +1.
Mihai Morcov
Essa deve ser a resposta certa se você precisar do método call a void.
db80
4

Eu tenho uma situação diferente, eu queria simular uma voidfunção para a primeira chamada e executá-la normalmente na segunda chamada.

Isso funciona para mim:

Mockito.doThrow(new RuntimeException("random runtime exception"))
       .doCallRealMethod()
       .when(spy).someMethod(Mockito.any());
Mohammed Elrashidy
fonte