Використання Mockito з кількома викликами до одного методу з однаковими аргументами


289

Чи є спосіб, щоб заглублений метод повертав різні об'єкти при наступних викликах? Я хотів би зробити це, щоб перевірити не визначені відповіді від ExecutorCompletionService. тобто перевірити, що незалежно від порядку повернення методів, результат залишається постійним.

Код, який я хочу перевірити, виглядає приблизно так.

// Create an completion service so we can group these tasks together
ExecutorCompletionService<T> completionService =
        new ExecutorCompletionService<T>(service);

// Add all these tasks to the completion service
for (Callable<T> t : ts)
    completionService.submit(request);

// As an when each call finished, add it to the response set.
for (int i = 0; i < calls.size(); i ++) {
    try {
        T t = completionService.take().get();
        // do some stuff that I want to test
    } catch (...) { }        
}

Відповіді:


254

Це можна зробити за допомогою thenAnswerметоду (при ланцюжку з when):

when(someMock.someMethod()).thenAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
});

Або використовуючи еквівалентний статичний doAnswerметод:

doAnswer(new Answer() {
    private int count = 0;

    public Object answer(InvocationOnMock invocation) {
        if (count++ == 1)
            return 1;

        return 2;
    }
}).when(someMock).someMethod();

635

Як щодо

when( method-call ).thenReturn( value1, value2, value3 );

Ви можете помістити стільки аргументів, скільки вам подобається, в дужках thenReturn, за умови, що вони все правильно. Перше значення повернеться при першому виклику методу, потім другому відповіді тощо. Останнє значення буде повертатися повторно, як тільки всі інші значення будуть використані.


4
Це буде працювати з глузуванням, але не зі шпигуном. Якщо вам потрібно не допустити виклику оригінального методу, вам потрібно виконати doAnswer (...). When (someSpy) .someMethod (...).
Юрій

6
@Юрі - не зовсім. Вам не потрібно doAnswerабо писати Answerу випадку, який ви згадуєте. Можна просто використовувати doReturn(...).when(someSpy).someMethod(...). Здається розумним припустити, що Емму цікавлять глузування, а не шпигуни, але, мабуть, я міг би додати щось до своєї відповіді, щоб промальовувати це. Дякуємо за коментар
Давуд ібн Карім

@DawoodibnKareem дозволяє сказати, що для першого дзвінка я хочу повернути значення, а для другого дзвінка хочу скинути виняток. Як це можна зробити?
Ріто

@Rito Будь ласка, прочитайте відповідь Володимира чи відповідь Raystorm. Вони обидва висвітлюють цю справу.
Давуд ібн Карім

Така славна відповідь.
wild_nothing

151

Як було зазначено раніше, майже всі дзвінки є прийнятними.

Так ви могли зателефонувати

when(mock.method()).thenReturn(foo).thenReturn(bar).thenThrow(new Exception("test"));

//OR if you're mocking a void method and/or using spy instead of mock

doReturn(foo).doReturn(bar).doThrow(new Exception("Test").when(mock).method();

Більше інформації в документації Mockito .


3
Дуже корисний! Що сталося б mock.methodу цьому прикладі було названо четвертий раз ? Я хочу щось подібне, поверніть foo перший раз, але поверніть смугу для ВСІХ решти.
javaPlease42

6
Кожне додаткове виклик у макеті поверне останнє "тоді Повернення" або останнє "Потім" Зростання "Дуже корисно
Франсуа Лакозьє

Дякую за чудові та прості інструкції. Ніколи до цього не знав. Я намагався знайти, як повернути два різні результати за двома однаковими дзвінками. Економте мені тонни часу.
CharlesC

68

Ви навіть можете ланцюгові doReturn()виклики методів, як це

doReturn(null).doReturn(anotherInstance).when(mock).method();

мило чи не так :)


4

Я реалізував MultipleAnswerклас, який допомагає мені підкреслювати різні відповіді на кожен дзвінок. Ось фрагмент коду:

private final class MultipleAnswer<T> implements Answer<T> {

    private final ArrayList<Answer<T>> mAnswers;

    MultipleAnswer(Answer<T>... answer) {
        mAnswers = new ArrayList<>();
        mAnswers.addAll(Arrays.asList(answer));
    }

    @Override
    public T answer(InvocationOnMock invocation) throws Throwable {
        return mAnswers.remove(0).answer(invocation);
    }
}

1
Чи можете ви ініціалізувати цей об'єкт коротким, простим і читабельним способом?
usr-local-ΕΨΗΕΛΩΝ

1

Далі може використовуватися як загальний метод для повернення різних аргументів на різні виклики методу. Єдине, що нам потрібно зробити, це нам потрібно передати масив із порядком, у якому об’єкти слід отримувати в кожному виклику.

@SafeVarargs
public static <Mock> Answer<Mock> getAnswerForSubsequentCalls(final Mock... mockArr) {
    return new Answer<Mock>() {
       private int count=0, size=mockArr.length;
       public Mock answer(InvocationOnMock invocation) throws throwable {
           Mock mock = null;
           for(; count<size && mock==null; count++){
                mock = mockArr[count];
           }

           return mock;    
       } 
    }
}

Вих. getAnswerForSubsequentCalls(mock1, mock3, mock2);поверне mock1 об’єкт під час першого дзвінка, mock3 об'єкт на другий виклик та mock2 об'єкт на третій виклик. Потрібно використовувати так, як when(something()).doAnswer(getAnswerForSubsequentCalls(mock1, mock3, mock2)); це майже схоже наwhen(something()).thenReturn(mock1, mock3, mock2);


1

Пов’язаний з відповіддю @ [Ігоря Ніколаєва] від 8 років тому, за допомогою значка Answerможна дещо спростити, використовуючи лямбда-вираз, наявний у Java 8.

when(someMock.someMethod()).thenAnswer(invocation -> {
    doStuff();
    return;
});

або простіше:

when(someMock.someMethod()).thenAnswer(invocation -> doStuff());

1

Стиль BDD:

import static org.mockito.BDDMockito.*;
...
    @Test
    void submit() {
        given(yourMock.yourMethod()).willReturn(1, 2, 3);

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.