Mockito: doAnswer против thenReturn

125

Я использую Mockito для последующего тестирования модулей. Я смущен , когда использовать doAnswerпротив thenReturn.

Кто-нибудь может мне подробно помочь? Пока что пробовал с thenReturn.

Раджкумар Тамбу
источник

Ответы:

167

Вам следует использовать thenReturnили, doReturnесли вы знаете возвращаемое значение во время имитации вызова метода. Это определенное значение возвращается, когда вы вызываете фиктивный метод.

thenReturn(T value) Задает возвращаемое значение, которое будет возвращено при вызове метода.

@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 используется, когда вам нужно выполнить дополнительные действия при вызове имитационного метода, например, когда вам нужно вычислить возвращаемое значение на основе параметров вызова этого метода.

Используйте, doAnswer()когда вы хотите заглушить метод void с помощью generic Answer.

Ответ определяет выполняемое действие и возвращаемое значение, которое возвращается при взаимодействии с макетом.

@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, но иногда вам следует вернуть некоторый сгенерированный внутренний код, не имеющий отношения к аргументам, например code = UUID.randomUUID(), я обнаружил, что это невозможно реализовать с помощью mockito.
zhuguowei
3
Когда ваш макет должен возвращать новый UUID для каждого вызова, вы должны реализовать Answerjust with return UUID.randomUUID();.
Роланд Вайследер
Могу ли я взять этот метод из новой инициализации ответа и поместить его в какой-нибудь метод, чтобы сделать код немного чище?
Line
3
@Line Answer- это функциональный интерфейс, поэтому в Java 8 вы можете заменить его лямбда-выражением. Если он недостаточно чистый, возможен любой другой обычный и необычный рефакторинг.
Роланд Вайследер
@ zhuguowei: вернуть некоторый сгенерированный внутренний код значения? Что ты имеешь в виду?
Саураб Патил
35

doAnswerи thenReturnсделайте то же самое, если:

  1. Вы используете Mock, а не Spy
  2. Метод, который вы используете, возвращает значение, а не метод void.

Давайте посмеемся над этим BookService

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

Вы можете заглушить getAuthor (), используя doAnswerи 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();

Обратите внимание, что при использовании doAnswerвы не можете передать метод when.

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

Итак, когда бы вы использовали doAnswerвместо thenReturn? Я могу придумать два варианта использования:

  1. Когда нужно "заглушить" метод void.

Используя doAnswer, вы можете выполнять некоторые дополнительные действия при вызове метода. Например, вызовите обратный вызов для 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. Когда вы используете Spy вместо Mock

При использовании when-thenReturn в Spy Mockito вызовет реальный метод и затем заглушит ваш ответ. Это может вызвать проблему, если вы не хотите вызывать реальный метод, как в этом примере:

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

Используя doAnswer, мы можем безопасно его заглушить.

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

На самом деле, если вы не хотите выполнять дополнительные действия при вызове метода, вы можете просто использовать doReturn.

List list = new LinkedList();
List spy = spy(list);
doReturn("java").when(spy).get(0);
assertEquals("java", spy.get(0));
aldok
источник
что, если издевательский метод недействителен?
Игорь Донин
1
Игорь, именно здесь doAnswer () появляется на сцене, и он рассказал об этом в ответе выше.
Саураб Патил
При использовании doAnswer(new Answer() { ... return null;}я получаю предупреждение в eclipse: «Ответ - это необработанный тип. Ссылки на общий тип Answer <T> должны быть параметризованы». Есть ли способ решить эту проблему (кроме игнорирования предупреждения c)?
LazR