Jak wyśmiewać puste metody za pomocą Mockito


937

Jak wyśmiewać metody typu void return?

Wdrożyłem wzorzec obserwatora, ale nie mogę kpić z Mockito, ponieważ nie wiem jak.

Próbowałem znaleźć przykład w Internecie, ale mi się nie udało.

Moja klasa wygląda następująco:

public class World {

    List<Listener> listeners;

    void addListener(Listener item) {
        listeners.add(item);
    }

    void doAction(Action goal,Object obj) {
        setState("i received");
        goal.doAction(obj);
        setState("i finished");
    }

    private string state;
    //setter getter state
} 

public class WorldTest implements Listener {

    @Test public void word{
    World  w= mock(World.class);
    w.addListener(this);
    ...
    ...

    }
}

interface Listener {
    void doAction();
}

System nie jest uruchamiany z próbą.

Chcę pokazać wyżej wymieniony stan systemu. I czyńcie według nich twierdzenia.


6
Uważaj, że metody void na próbach nic nie robią domyślnie!
— Linia

1
@Line, właśnie tego szukałem. Wydaje się to oczywiste, kiedy to powiesz. Ale uwypukla zasadę kpiny: wystarczy kpić z metod kpionych klas pod kątem ich efektów, takich jak wartość zwracana lub wyjątek. Dziękuję Ci!
— allenjom

Odpowiedzi:


1144

Spójrz na dokumenty Mockito API . Ponieważ powiązany dokument wspomina (punkt 12), można użyć dowolnego z doThrow(), doAnswer(), doNothing(), doReturn()rodzina metod z Mockito ramach drwić void metod.

Na przykład,

Mockito.doThrow(new Exception()).when(instance).methodName();

lub jeśli chcesz połączyć to z dalszym działaniem,

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName();

Zakładając, że próbujesz kpić z setera setState(String s)w poniższym klasowym świecie, kod używa doAnswermetody do kpienia z setState.

World  mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() {
    public Void answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      System.out.println("called with arguments: " + Arrays.toString(args));
      return null;
    }
}).when(mockWorld).setState(anyString());

8
@qualidafial: Tak, myślę, że parametryzacja do Void byłaby lepsza, ponieważ lepiej pokazuje, że nie jestem zainteresowany typem zwrotu. Nie byłam świadoma tego konstruktu, dzięki za wskazanie go.
— sateesh

2
doThrow jest teraz # 5 (również dla mnie za pomocą doThrow to naprawiono komunikat „typ void jest tutaj niedozwolony”, dla obserwujących ...)
— rogerdpack 21.12.12

@qualidafial: Myślę, że typem zwrotnym wywołania Answer.answer nie jest to, co jest zwracane do oryginalnej metody, to to, co jest zwracane do wywołania doAnswer, prawdopodobnie jeśli chcesz zrobić coś innego z tą wartością w teście.
— dwanaście17

1
:( w próbie Mock w wersji 16.0.1 RateLimiter.java w guava doNothing (). when (mockLimiterReject) .setRate (100) powoduje wywołanie funkcji setRate RateLimiter, co powoduje, że nullpointer jest z nullpointer, ponieważ mutex jest z jakiegoś powodu zerowany więc nie wyśmiewał mojej metody setRate :(, ale nazwał ją :(
— Dean Hiller

2
@DeanHiller zauważa, że setRate()jest finali dlatego nie można go wyśmiewać. Zamiast tego spróbuj create()instancji, która robi to, czego potrzebujesz. Nie powinno być potrzeby kpić RateLimiter.
— dimo414,

113

Myślę, że znalazłem prostszą odpowiedź na to pytanie, aby wywołać prawdziwą metodę tylko dla jednej metody (nawet jeśli ma ona nieważny zwrot), możesz to zrobić:

Mockito.doCallRealMethod().when(<objectInstance>).<method>();
<objectInstance>.<method>();

Lub możesz wywołać prawdziwą metodę dla wszystkich metod tej klasy, wykonując następujące czynności:

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS);

13
Oto prawdziwa odpowiedź tutaj. Metoda spy () działa dobrze, ale na ogół jest zarezerwowana, gdy chcesz, aby obiekt robił większość wszystkiego normalnie.
— biggusjimmus,

1
Co to znaczy? Czy faktycznie wywołujesz metody? Tak naprawdę nigdy wcześniej nie korzystałem z mockito.
— obesechicken13

Tak, makieta wywoła prawdziwe metody. Jeśli używasz @Mock, możesz określić to samo za pomocą: @Mock (answer = Answers.CALLS_REAL_METHODS), aby uzyskać te same wyniki.
— Ale

70

Dodając do tego, co powiedział @sateesh, gdy chcesz po prostu wyszydzić metodę void, aby zapobiec wywołaniu testu przez test, możesz użyć Spynastępującego sposobu:

World world = new World();
World spy = Mockito.spy(world);
Mockito.doNothing().when(spy).methodToMock();

Kiedy chcesz uruchomić test, upewnij się, że wywołujesz metodę testową na spyobiekcie, a nie na worldobiekcie. Na przykład:

assertEquals(0,spy.methodToTestThatShouldReturnZero());

57

Rozwiązaniem tak zwanego problemu jest użycie spy Mockito.spy (...) zamiast mock Mockito.mock (..) .

Szpieg umożliwia nam częściowe kpiny. Mockito jest dobry w tej kwestii. Ponieważ masz klasę, która nie jest kompletna, w ten sposób kpisz z wymaganego miejsca w tej klasie.


3
Natknąłem się tutaj, ponieważ miałem podobny problem (również przypadkowo testowałem interakcję podmiot / obserwator). Już używam szpiega, ale chcę, aby metoda „SubjectChanged” zrobiła coś innego. Mógłbym użyć `Verify (observer) .subjectChanged (subject) tylko po to, aby zobaczyć, że metoda została wywołana. Ale z jakiegoś powodu wolałbym zastąpić tę metodę. Aby to zrobić, połączenie podejścia Sateesh i twojej odpowiedzi tutaj było
— właściwą

32
Nie, robienie tego w rzeczywistości nie pomoże w wyśmiewaniu metod pustki. Sztuką jest użycie jednej z czterech statycznych metod Mockito wymienionych w odpowiedzi sateesh.
— Dawood ibn Kareem

2
@Gurnard za pytanie przyjrzeć się tej stackoverflow.com/questions/1087339/... .
— ibrahimyilmaz

Ta zgoda faktycznie działa.
— Nilotpal

34

Po pierwsze: zawsze powinieneś importować mockito static, w ten sposób kod będzie znacznie bardziej czytelny (i intuicyjny):

import static org.mockito.Mockito.*;

Za częściowe kpiny i zachowanie oryginalnej funkcjonalności mockito oferuje „Szpiega”.

Możesz użyć tego w następujący sposób:

private World world = spy(World.class);

Aby wyeliminować wykonanie metody, możesz użyć czegoś takiego:

doNothing().when(someObject).someMethod(anyObject());

aby nadać metodzie niestandardowe zachowanie użyj „when” z „thenReturn”:

doReturn("something").when(this.world).someMethod(anyObject());

Więcej przykładów można znaleźć doskonałe próbki mockito w dokumencie.


4
Co ma związek z importem statycznym, aby był bardziej czytelny?
— jsonbourne

2
dosłownie nic.
— specializt

2
Myślę, że to kwestia gustu, po prostu lubię, aby instrukcja wyglądała (prawie) jak angielskie zdanie, a Class.methodname (). Coś () vs. methodname (). Coś jest mniej czytelne.
— fl0w

1
Jeśli pracujesz w zespole, importowanie statyczne sprawia, że ​​trudno jest osiołowi zrozumieć, skąd pochodzi metoda, ponieważ w imporcie często występuje symbol wieloznaczny.
— LowKeyEnergy

to prawda, jednak w przypadku kodu testowego i Mockito powinno to być oczywiste i nie stanowić problemu.
— fl0w

26

Jak wyśmiewać puste metody za pomocą mockito - są dwie opcje:

  1. doAnswer - Jeśli chcemy, aby nasza metoda kpiny z pustki coś zrobiła (kpij z zachowania, mimo że jest nieważna).
  2. doThrow- Wtedy jest, Mockito.doThrow()jeśli chcesz rzucić wyjątek od szyderczej metody void.

Poniżej znajduje się przykład jego użycia (nie jest to idealny przypadek użycia, ale chciałem tylko zilustrować podstawowe użycie).

@Test
public void testUpdate() {

    doAnswer(new Answer<Void>() {

        @Override
        public Void answer(InvocationOnMock invocation) throws Throwable {
            Object[] arguments = invocation.getArguments();
            if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) {

                Customer customer = (Customer) arguments[0];
                String email = (String) arguments[1];
                customer.setEmail(email);

            }
            return null;
        }
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@Test(expected = RuntimeException.class)
public void testUpdate_throwsException() {

    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class));

    // calling the method under test
    Customer customer = service.changeEmail("old@test.com", "new@test.com");

}
}

Można znaleźć więcej szczegółów na temat sposobu drwić i przetestować void metod z Mockito w moim poście Jak Mock z Mockito (obszerny przewodnik z przykładami)


1
Świetny przykład. Uwaga: w java 8 może być trochę przyjemniej użyć lambda zamiast anonimowej klasy: wywołanie doAnswer ((Odpowiedź <Void>) -> {// CODE}). When (mockInstance) .add (metoda ()); ”
— miwe,

16

Dodanie kolejnej odpowiedzi do paczki (bez zamierzonej gry słów) ...

Musisz wywołać metodę doAnswer, jeśli nie możesz \ nie chcieć używać szpiega. Jednak niekoniecznie musisz rzucić własną odpowiedź . Istnieje kilka domyślnych implementacji. W szczególności CallsRealMethods .

W praktyce wygląda to tak:

doAnswer(new CallsRealMethods()).when(mock)
        .voidMethod(any(SomeParamClass.class));

Lub:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock)
        .voidMethod(any(SomeParamClass.class));

16

W Javie 8 można to uczynić nieco czystszym, zakładając, że masz statyczny import dla org.mockito.Mockito.doAnswer:

doAnswer(i -> {
  // Do stuff with i.getArguments() here
  return null;
}).when(*mock*).*method*(*methodArguments*);

Jest return null;to ważne i bez tego kompilacja nie powiedzie się z pewnymi dość niejasnymi błędami, ponieważ nie będzie w stanie znaleźć odpowiedniego zastępowania doAnswer.

Na przykład, ExecutorServicektóry natychmiast wykonuje wszystkie Runnableprzekazane do, execute()można zaimplementować za pomocą:

doAnswer(i -> {
  ((Runnable) i.getArguments()[0]).run();
  return null;
}).when(executor).execute(any());

2
W jednym wierszu: Mockito.doAnswer ((i) -> null) .when (instance) .method (any ());
— Akshay Thorve

@AkshayThorve To nie działa, gdy naprawdę chcesz robić rzeczy z i chociaż.
— Tim B

7

Myślę, że twoje problemy wynikają z twojej struktury testowej. Trudno mi było łączyć drwiny z tradycyjną metodą implementacji interfejsów w klasie testowej (tak jak tutaj zrobiono).

Jeśli zaimplementujesz detektor jako próbkę, możesz następnie zweryfikować interakcję.

Listener listener = mock(Listener.class);
w.addListener(listener);
world.doAction(..);
verify(listener).doAction();

To powinno cię przekonać, że „świat” postępuje właściwie.


0

Używanie Mockito.doThrow jak w:

Mockito.doThrow(new Exception()).when(instance).methodName();

możesz spróbować tego miłego przykładu:

public void testCloseStreamOnException() throws Exception {
    OutputStream outputStream = Mockito.mock(OutputStream.class);
    IFileOutputStream ifos = new IFileOutputStream(outputStream);
    Mockito.doThrow(new IOException("Dummy Exception")).when(outputStream).flush();
    try {
      ifos.close();
      fail("IOException is not thrown");
    } catch (IOException ioe) {
      assertEquals("Dummy Exception", ioe.getMessage());
    }
    Mockito.verify(outputStream).close();
  }

Źródło: http://apisonar.com/java-examples/org.mockito.Mockito.doThrow.html#Example-19

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.