Mockito: doAnswer Vs thenReturn

125

Używam Mockito do późniejszego serwisowania testów jednostkowych. Jestem zdezorientowany, gdy w użyciu doAnswervs thenReturn.

Czy ktoś może mi pomóc szczegółowo? Do tej pory próbowałem tego z thenReturn.

Rajkumar Thambu
źródło

Odpowiedzi:

167

Powinieneś użyć thenReturnlub, doReturngdy znasz wartość zwracaną w momencie mockowania wywołania metody. Ta zdefiniowana wartość jest zwracana po wywołaniu metody mockowanej.

thenReturn(T value) Ustawia wartość zwracaną, która ma zostać zwrócona po wywołaniu metody.

@Test
public void test_return() throws Exception {
    Dummy dummy = mock(Dummy.class);
    int returnValue = 5;

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenReturn(returnValue);
    doReturn(returnValue).when(dummy).stringLength("dummy");
}

Answer jest używany, gdy trzeba wykonać dodatkowe akcje przy wywołaniu metody mockowanej, np. gdy trzeba obliczyć wartość zwracaną na podstawie parametrów wywołania tej metody.

Użyj, doAnswer()gdy chcesz zablokować metodę void za pomocą generic Answer.

Odpowiedź określa akcję, która jest wykonywana i wartość zwracaną, która jest zwracana podczas interakcji z makietą.

@Test
public void test_answer() throws Exception {
    Dummy dummy = mock(Dummy.class);
    Answer<Integer> answer = new Answer<Integer>() {
        public Integer answer(InvocationOnMock invocation) throws Throwable {
            String string = invocation.getArgumentAt(0, String.class);
            return string.length() * 2;
        }
    };

    // choose your preferred way
    when(dummy.stringLength("dummy")).thenAnswer(answer);
    doAnswer(answer).when(dummy).stringLength("dummy");
}
Roland Weisleder
źródło
cześć @Roland Weisleder, ale czasami powinieneś zwrócić jakiś kod wewnętrzny wygenerowany przez wartość i nie ma nic wspólnego z argumentami, np. nie code = UUID.randomUUID()mogę tego zaimplementować mockito.
zhuguowei
3
Kiedy twój makieta powinien zwrócić nowy UUID dla każdego wywołania, zaimplementowałbyś Answertylko z return UUID.randomUUID();.
Roland Weisleder,
Czy mogę wziąć tę metodę z inicjalizacji nowej odpowiedzi i umieścić ją w jakiejś metodzie, aby kod był nieco bardziej przejrzysty?
Linia
3
@Linia Answer to funkcjonalny interfejs, więc w Javie 8 można go zastąpić wyrażeniem lambda. Jeśli nie jest wystarczająco czysty, możliwa jest inna zwykła i nietypowa refaktoryzacja.
Roland Weisleder
@ zhuguowei: zwrócić jakąś wygenerowaną wartość kodu wewnętrznego? Co przez to rozumiesz?
Saurabh Patil
35

doAnsweri thenReturnzrób to samo, jeśli:

  1. Używasz Mocka, a nie Szpiega
  2. Metoda, którą usuwasz, zwraca wartość, a nie metoda void.

Kpimy z tego BookService

public interface BookService {
    String getAuthor();
    void queryBookTitle(BookServiceCallback callback);
}

Możesz odgiąć getAuthor () używając doAnsweri thenReturn.

BookService service = mock(BookService.class);
when(service.getAuthor()).thenReturn("Joshua");
// or..
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return "Joshua";
    }
}).when(service).getAuthor();

Zauważ, że podczas używania doAnswernie możesz przekazać metody when.

// Will throw UnfinishedStubbingException
doAnswer(invocation -> "Joshua").when(service.getAuthor());

Kiedy więc użyjesz doAnswerzamiast thenReturn? Przychodzą mi do głowy dwa przypadki użycia:

  1. Kiedy chcesz „zablokować” void method.

Używając doAnswer, możesz wykonać dodatkowe czynności przy wywołaniu metody. Na przykład wyzwalaj wywołanie zwrotne w queryBookTitle.

BookServiceCallback callback = new BookServiceCallback() {
    @Override
    public void onSuccess(String bookTitle) {
        assertEquals("Effective Java", bookTitle);
    }
};
doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        BookServiceCallback callback = (BookServiceCallback) invocation.getArguments()[0];
        callback.onSuccess("Effective Java");
        // return null because queryBookTitle is void
        return null;
    }
}).when(service).queryBookTitle(callback);
service.queryBookTitle(callback);
  1. Kiedy używasz Szpiega zamiast Mocka

Podczas korzystania z when-thenReturn on Spy Mockito wywoła prawdziwą metodę, a następnie zgubi twoją odpowiedź. Może to spowodować problem, jeśli nie chcesz wywoływać prawdziwej metody, jak w tym przykładzie:

List list = new LinkedList();
List spy = spy(list);
// Will throw java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
when(spy.get(0)).thenReturn("java");
assertEquals("java", spy.get(0));

Używając doAnswer, możemy go bezpiecznie zablokować.

List list = new LinkedList();
List spy = spy(list);
doAnswer(invocation -> "java").when(spy).get(0);
assertEquals("java", spy.get(0));

Właściwie, jeśli nie chcesz wykonywać dodatkowych czynności przy wywołaniu metody, możesz po prostu użyć doReturn.

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
aldok
źródło
co jeśli wyszydzana metoda jest nieważna?
Igor Donin
1
Igor, właśnie w tym miejscu pojawia się metoda doAnswer () i omówił to w powyższej odpowiedzi.
Saurabh Patil
Podczas korzystania z funkcji doAnswer(new Answer() { ... return null;}i otrzymuję ostrzeżenie w eclipse o treści „Odpowiedź jest typu surowego. Odniesienia do typu ogólnego Answer <T> powinny być sparametryzowane”. Czy istnieje sposób na rozwiązanie tego problemu (poza ignorowaniem ostrzeżenia o c)?
LazR