it-swarm.com.ru

Обратные вызовы с Mockito

У меня есть код

service.doAction(request, Callback<Response> callback);

Как я могу с помощью Mockito захватить объект обратного вызова и вызвать callback.reply (x)

56
Kurru

Вы хотите настроить объект Answer, который это делает. Ознакомьтесь с документацией Mockito по адресу https://static.javadoc.io/org.mockito/mockito-core/2.8.47/org/mockito/Mockito.html#answer_stubs

Вы могли бы написать что-то вроде

when(mockService.doAction(any(Request.class), any(Callback.class))).thenAnswer(
    new Answer<Object>() {
        Object answer(InvocationOnMock invocation) {
            ((Callback<Response>) invocation.getArguments()[1]).reply(x);
            return null;
        }
});

(конечно, заменив x на то, что должно быть)

68
Dawood ibn Kareem

Подумайте об использовании ArgumentCaptor , который в любом случае ближе соответствует "захватить [bing] объект обратного вызова".

/**
 * Captor for Response callbacks. Populated by MockitoAnnotations.initMocks().
 * You can also use ArgumentCaptor.forClass(Callback.class) but you'd have to
 * cast it due to the type parameter.
 */
@Captor ArgumentCaptor<Callback<Response>> callbackCaptor;

@Test public void testDoAction() {
  // Cause service.doAction to be called

  // Now call callback. ArgumentCaptor.capture() works like a matcher.
  verify(service).doAction(eq(request), callbackCaptor.capture());

  assertTrue(/* some assertion about the state before the callback is called */);

  // Once you're satisfied, trigger the reply on callbackCaptor.getValue().
  callbackCaptor.getValue().reply(x);

  assertTrue(/* some assertion about the state after the callback is called */);
}

Хотя Answer является хорошей идеей, когда обратный вызов должен немедленно возвращаться (читай: синхронно), он также создает накладные расходы на создание анонимного внутреннего класса и небезопасное приведение элементов из invocation.getArguments()[n] к нужному типу данных. Также требуется, чтобы вы делали какие-либо утверждения о состоянии системы перед обратным вызовом из ОТВЕТА в ответ, что означает, что ваш ответ может увеличиваться в размере и объеме.

Вместо этого обработайте ваш обратный вызов асинхронно: захватите объект обратного вызова, переданный вашей службе, с помощью ArgumentCaptor. Теперь вы можете сделать все свои утверждения на уровне тестового метода и вызвать reply, когда захотите. Это особенно полезно, если ваша служба отвечает за множественные одновременные обратные вызовы, потому что у вас больше контроля над порядком возврата обратных вызовов.

45
Jeff Bowman

Если у вас есть метод, как: -

public void registerListener(final IListener listener) {
    container.registerListener(new IListener() {
        @Override
        public void beforeCompletion() {
        }

        @Override
        public void afterCompletion(boolean succeeded) {
            listener.afterCompletion(succeeded);
        }
    });
}

Затем следующим способом вы можете легко издеваться над вышеуказанным методом: -

@Mock private IListener listener;

@Test
public void test_registerListener() {
    target.registerListener(listener);

    ArgumentCaptor<IListener> listenerCaptor =
            ArgumentCaptor.forClass(IListener.class);

    verify(container).registerListener(listenerCaptor.capture());

    listenerCaptor.getValue().afterCompletion(true);

    verify(listener).afterCompletion(true);
}

Я надеюсь, что это может кому-то помочь, так как я потратил много времени на выяснение этого решения

5
Sumit Kumar Saha
when(service.doAction(any(Request.class), any(Callback.class))).thenAnswer(
    new Answer() {
    Object answer(InvocationOnMock invocation) {
        Callback<Response> callback =
                     (Callback<Response>) invocation.getArguments()[1];
        callback.reply(/*response*/);
    }
});
2
Garrett Hall