Подтвердить что ты не робот

Вызов обратных вызовов с помощью Mockito

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

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

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

4b9b3361

Ответ 1

Вы хотите настроить объект 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 тем, что должно быть, конечно)

Ответ 2

Подумайте об использовании 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] в тип данных вы хотите. Это также требует, чтобы вы делали какие-либо утверждения о состоянии перед обратным вызовом системы из WITHIN the Answer, что означает, что ваш ответ может увеличиваться по размеру и объему.

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

Ответ 3

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*/);
    }
});

Ответ 4

Если у вас есть способ: -

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

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