У меня есть код
service.doAction(request, Callback<Response> callback);
Как я могу использовать Mockito для захвата объекта обратного вызова и вызывать callback.reply(x)
У меня есть код
service.doAction(request, Callback<Response> callback);
Как я могу использовать Mockito для захвата объекта обратного вызова и вызывать callback.reply(x)
Вы хотите настроить объект 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
тем, что должно быть, конечно)
Подумайте об использовании 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
вызов, когда вы выберете. Это особенно полезно, если ваша служба отвечает за несколько одновременных обратных вызовов, потому что вы больше контролируете порядок возврата обратных вызовов.
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*/);
}
});
Если у вас есть способ: -
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);
}
Я надеюсь, что это может помочь кому-то, поскольку я потратил много времени на то, чтобы выяснить это решение