Calling callbacks with Mockito

2020-01-25 05:52发布

I have some code

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

How can I using Mockito grab the callback object, and call callback.reply(x)

4条回答
冷血范
2楼-- · 2020-01-25 06:26

If you have a method like:-

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

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

Then following way you can mock the above method easily :-

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

I hope this might help someone, as i had spend lot of time in figuring out this solution

查看更多
走好不送
3楼-- · 2020-01-25 06:41

You want to set up an Answer object that does that. Have a look at the Mockito documentation, at https://static.javadoc.io/org.mockito/mockito-core/2.8.47/org/mockito/Mockito.html#answer_stubs

You might write something like

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

(replacing x with whatever it ought to be, of course)

查看更多
太酷不给撩
4楼-- · 2020-01-25 06:46
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*/);
    }
});
查看更多
The star\"
5楼-- · 2020-01-25 06:49

Consider using an ArgumentCaptor, which in any case is a closer match to "grab[bing] the callback object".

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

While an Answer is a good idea when the callback needs to return immediately (read: synchronously), it also introduces the overhead of creating an anonymous inner class, and unsafely casting the elements from invocation.getArguments()[n] to the data type you want. It also requires you to make any assertions about the pre-callback state of the system from WITHIN the Answer, which means that your Answer may grow in size and scope.

Instead, treat your callback asynchronously: Capture the Callback object passed to your service using an ArgumentCaptor. Now you can make all of your assertions at the test method level and call reply when you choose. This is of particular use if your service is responsible for multiple simultaneous callbacks, because you have more control over the order in which the callbacks return.

查看更多
登录 后发表回答