Java Mockito and Mocking a Callback

It looks hard to mock something like this:

void doSomething(MyCallback callback) {
    // calculate the answer
    // ...
    callback.callMe(someValue);
}

The code above is a bit tricky to mock. It would be easy if it were

Result doSomething() {
    // calculate the answer
    // ...
    return someValue;
}

In this case, the interface which has the doSomething function in it could be easily mocked with mockito thusly:

when(someMock.doSomething()).thenReturn(mockResult);

Which means that code that’s depending on this interface can be called against a mock and the result just passes back. But if we don’t want the real object to really calculate the answer and call a callback and simply want to stub that the callback will be called with a test value to keep our test moving, then there’s got to be some magic to make that happen. Mockito provides an answer:

// assume our doSomething function is in the interface behind someMock
// and we're trying to rig it so that the answer 42 is going to get put into
// the callback when the calling code uses our mock object
doAnswer(new Answer<Object>() {
    // the answer function is where we can get our mock to DO something
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        // our mock function has one argument - the 0'th input is the mock
        // the 1'th input is the input to the mock - in this case, the callback we
        // are about to plug the mock answer into - "callMe" is its callback function
        // and 42 is the test value we're going to answer with
        ((MyCallback)invocation.getArguments()[1]).callMe(42);

        // it's a void function, so the return is immaterial - similarly the whole
        // class is given a type parameter of Object as there's no real return type
        return null;
    }
// this last line hooks the doSomething call of the someMock to the execution of an answer defined
// in this anonymous inner class
// the any matcher here mean's we're not choosy about the input in this instance
// though we could have different answers for different callback targets
// by putting in more specific matchers
}).when(someMock).doSomething(any(MyCallback.class));

And you can parameterise the above if you want… and it works… but it’s clumsy.

EDIT: 2016

Thanks to the wonderful Mockito team, who accepted a pull request from me, there’s now a nicer way of doing the above. For a start, you could have already used a Java 8 lambda, since the Answer interface is essentially a functional interface. It was already possible to do

doAnswer(invocationOnMock -> {
        ((MyCallback)invocation.getArguments()[1]).callMe(42);
        return null;
    }).when(someMock).doSomething(any(MyCallback.class));

Bear in mind that this is just the same as the previous example, but it looks nicer as the anonymous inner class is replaced with a lambda, and I’ve stripped out the comments, which makes it look cleaner. The ugly bit is:

((MyCallback)invocation.getArguments()[1]).callMe(42);
        return null;

Which is essentially some casting and returning boilerplate we’d rather live without. In Mockito 2, there’s now an AdditionalAnswers helper class to let you use type inference. The ideal technique would be to have your callback mocking in its own function you could wire in with a function reference. Or, you could use a little casting to do it directly.

// assuming static import of AdditionalAnswers

// with function reference
doAnswer(answerVoid(this::mockCallback))
    .when(someMock).doSomething(any(MyCallback.class));

void mockCallback(MyCallback callback) {
    callback.callMe(42);
}

// without function reference
doAnswer(answerVoid((MyCallback myCallback) -> myCallback.callMe(42)))
    .when(someMock).doSomething(any(MyCallback.class));

The above also works for when the function needs to return something. You use answer, rather than answerVoid to help build your answer implicitly and link it to a strongly typed lambda that can take up to 5 parameters.

See http://static.javadoc.io/org.mockito/mockito-core/2.2.11/org/mockito/Mockito.html#37 for more information.

Advertisements

Software developer, stand-up comedian, musician, writer, jolly big cheer-monkey, skeptical thinker, Doctor Who fan, lover of fine sounds.

Tagged with: , ,
Posted in Uncategorized
2 comments on “Java Mockito and Mocking a Callback
  1. Jesus Almaral says:

    Hi, thanks for the post, it works!, but what if I my code follows some logic after getting the callback, for example:

    mockJsonObject.put(“valid”, true);

    doAnswer(new Answer() {
    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
    JsonObjectResponseListener callback =
    (JsonObjectResponseListener) invocation.getArguments()[1];
    callback.onResponse(mockJsonObject);

    // THE CODE DOES SOMETHING DEPENDING ON THE “valid” VALUE OF mockJsonObject, for example.
    assertEquals(mockJsonObject.getString(“valid”), true)

    return null;
    }

    In my case the test finishes no matter what I put after the code, how can I get the mocked object?

  2. ashleyfrieze says:

    I’m not quite sure the issue you’re facing here. Your doAnswer statement above is missing the when section. The syntax is doAnswer(…).when(object).someFunction();

    Perhaps post a more complete example as a Gist on GitHub and link it here, or post a question on StackOverflow.

    Also, if you’re using Java 8, see the new mechanism for custom answers available in Mockito 2.1 onwards – http://static.javadoc.io/org.mockito/mockito-core/2.6.8/org/mockito/Mockito.html#37

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: