4

In mockito, I want to mock a method that returns some value and also has to invoke a callback

For example, here is the service method:

String fetchString(Callback<String> callback);

I want the return value to happen before the callback is invoked. I looked into using Mockito.doAnswer(..) but can't seem to figure out how to make the method invoke the callback after the return statement. Example:

when(mockService.fetchString(any(Callback.class)).thenAnswer(
    new Answer<String>() {
        String answer(InvocationOnMock invocation) {
            ((Callback<String>) invocation.getArguments()[0]).onResult("callback string");
            return "return string";
        }
    });

As you can see in the example above: the callback is invoked before the value is returned. This does't test asynchronous callbacks properly. Is there a way to make the callback method be called after the value is returned?

I know that argumentCaptor can be used here, but is there an alternative that doesn't involve manually calling the callback?

Something that is a combination of doAnswer(..) and thenReturn(..)?

Prem
  • 3,373
  • 7
  • 29
  • 43
  • 1
    Use `ArgumentCaptor` to get instance of callback and invoke it when you need it. – talex Jul 28 '17 at 14:01
  • @Talex `ArgumentCaptor` can be used, but only after the method under test has been called. I think he wants to invoke that callback while the method under test is still running. – Maciej Kowalski Jul 28 '17 at 14:08
  • He specifically state "callback method be called immediately after the value is returned". – talex Jul 28 '17 at 14:09
  • ok but there may be more code ahead after that mocked method is called – Maciej Kowalski Jul 28 '17 at 14:11
  • 1
    Could you please describe your use case? What is it that you do with the callback afterwards? – Morfic Jul 28 '17 at 14:22
  • If you are trying to test behavior that occurs after the invoked method, then this isn't really appropriate as a unit test of the fetchString method. How does the actual fetchString method dispatch the callback asynchronously? If anything, a unit test should just test that the callback was dispatched properly, and then a separate unit test would cover the behavior of the callback itself – Kevin Welker Jul 28 '17 at 14:35
  • Edited.. The callback does not need to be called immediately but it needs to be called after the value is returned – Prem Jul 28 '17 at 14:43
  • There are two solutions in [this existing answer](https://stackoverflow.com/q/13616547/1426891), one using `Answer` and one using `ArgumentCaptor`. Marking as duplicate because the questions are pretty close, and the answer is there, though I can also see keeping them separate because yours has a constraint that the original doesn't that makes only one of the answers valid. Let me know if you'd rather I reopen. – Jeff Bowman Jul 28 '17 at 19:31

1 Answers1

2

The best way to achieve this is to use ArgumentCaptor, as mentioned by @talex.

The way I used it is:

Service method:

String fetchString(Callback<String> callback);

JUnit Test:

ArgumentCaptor<Callback> captor = ArgumentCaptor.forClass(Callback.class);
when(mockService.fetchString(captor.capture()).thenReturn("return string");

String answer = mockService.fetchString(callbackToTest);
// callback is invoked after the service method returned the value
captor.getValue().onResult("callback string");

assertEquals("return string", answer);
verify(callbackToTest).onResult(eq("callback string"));
Prem
  • 3,373
  • 7
  • 29
  • 43