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.

ibrahimyilmaz
źródło
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());
sateesh
źródło
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);
MarcioB
źródło
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());
Omri374
źródło
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.

ibrahimyilmaz
źródło
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.

fl0w
źródło
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("[email protected]", "[email protected]");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("[email protected]")));

}

@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("[email protected]", "[email protected]");

}
}

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)

Dilini Rajapaksha
źródło
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));
javamonkey79
źródło
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());
Tim B.
źródło
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.

Ashley
źródło
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

APISonar
źródło