Symulacja pierwszego połączenia kończy się niepowodzeniem, drugie połączenie się udaje

119

Chcę użyć Mockito do przetestowania (uproszczonego) kodu poniżej. Nie wiem, jak powiedzieć Mockito, żeby za pierwszym razem zawiódł, a za drugim odniósł sukces.

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

Mogę skonfigurować test sukcesu za pomocą:

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

Oraz test awarii z:

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

Ale jak mogę sprawdzić, czy jeśli raz (lub dwa) się nie powiedzie, a potem się powiedzie, to dobrze?

jb.
źródło

Odpowiedzi:

250

Z dokumentów :

Czasami potrzebujemy stub z inną zwracaną wartością / wyjątkiem dla tego samego wywołania metody. Typowym przypadkiem użycia może być mockowanie iteratorów. Oryginalna wersja Mockito nie miała tej funkcji promującej proste kpiny. Na przykład zamiast iteratorów można by użyć Iterowalnych lub po prostu kolekcji. Oferują naturalne sposoby zarostu (np. Użycie prawdziwych kolekcji). W rzadkich sytuacjach przydatne może być jednak blokowanie kolejnych połączeń:

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

Więc w twoim przypadku chciałbyś:

when(myMock.doTheCall())
   .thenReturn("You failed")
   .thenReturn("Success");
Jon Skeet
źródło
25
To wskazało mi właściwy kierunek (dzięki) za mój scenariusz, który dotyczył metod void - w tym przypadku musisz użyć stylu alternatywnego ...doThrow(new RuntimeException()).doNothing().when(myMock).doTheCall();
haggisandchips
38

Najkrótszy sposób na napisanie tego, co chcesz, to

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

Kiedy podasz w thenReturnten sposób wiele argumentów, każdy argument zostanie użyty najwyżej raz, z wyjątkiem ostatniego argumentu, który jest używany tyle razy, ile potrzeba. Na przykład w tym przypadku, jeśli wykonasz połączenie 4 razy, otrzymasz komunikat „Sukces”, „zawiodłeś”, „zawiodłeś”, „zawiodłeś”.

Dawood ibn Kareem
źródło
22

Ponieważ komentarz, który się z tym wiąże, jest trudny do odczytania, dodam sformatowaną odpowiedź.

Jeśli próbujesz to zrobić za pomocą voidfunkcji, która po prostu zgłasza wyjątek, po którym następuje krok braku zachowania, możesz zrobić coś takiego:

Mockito.doThrow(new Exception("MESSAGE"))
            .doNothing()
            .when(mockService).method(eq());
anoneironaut
źródło
4

Aby dodać do tej i tej odpowiedzi, możesz również użyć pętli do łączenia w łańcuch udawanych połączeń. Jest to przydatne, jeśli chcesz kilka razy kpić z tego samego lub w jakiś sposób.

Np. (Aczkolwiek naciągany):

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
źródło
Właściwie nie wiedziałem o tym Stubberze i uznałem go za przydatny, +1.
Mihai Morcov
To powinna być właściwa odpowiedź, jeśli potrzebujesz wywołać metodę void.
db80
4

Mam inną sytuację, chciałem mockować voidfunkcję dla pierwszego wywołania i uruchomić ją normalnie przy drugim wywołaniu.

To działa dla mnie:

Mockito.doThrow(new RuntimeException("random runtime exception"))
       .doCallRealMethod()
       .when(spy).someMethod(Mockito.any());
Mohammed Elrashidy
źródło