Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito when().thenReturn() throws nullpointerExceptions

I'm trying to use Mockito to create a Mock object that gets returned from a Mock object. Specifically, I'm trying to mock a PlayerConnection object that my program can use to retrieve an IP address.

You can find more about this PlayerConnection object here. It returns an InetSocketAddress which can then return an InetAddress that can return a String with the IP of the player. But I can't get that far, because my first when(class.function()).thenReturn(returnVariable) throws a NullPointerException. Here's my code:

/**
 * Creates a partial mock of a connection that can return an ip address.
 * 
 * @param String
 *            The IP to return when the connection gets asked.
 * @return
 */
private PlayerConnection newConnection(String ipString)
{
    PlayerConnection playerConnection = mock(PlayerConnection.class);
    InetSocketAddress inetSocketAddress = mock(InetSocketAddress.class);
    InetAddress inetAddress = mock(InetAddress.class);

    when(playerConnection.getAddress()).thenReturn(inetSocketAddress);
    when(inetSocketAddress.getAddress()).thenReturn(inetAddress);
    when(inetAddress.getHostAddress()).thenReturn(ipString);

    return playerConnection;
}

And here's the stack trace, occuring at when(playerConnection.getAddress()).thenReturn(inetSocketAddress):

Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.001 sec <<< FAILURE!
ruleResponseTest(com.github.heartsemma.communitywall.ConnectionChecks.RuleManagerTest)  Time elapsed: 0.001 sec  <<< ERROR!
java.lang.NullPointerException
        at java.net.InetSocketAddress$InetSocketAddressHolder.access$500(InetSocketAddress.java:56)
        at java.net.InetSocketAddress.getAddress(InetSocketAddress.java:334)
        at com.github.heartsemma.communitywall.ConnectionChecks.RuleManagerTest.newConnection(RuleManagerTest.java:99)
        at com.github.heartsemma.communitywall.ConnectionChecks.RuleManagerTest.ruleResponseTest(RuleManagerTest.java:44)

Edit:

I have switched my stubs to doReturn().when().function() instead of when().thenReturn() to stop the NullPointerExceptions, and it did, but now I am getting custom UnfinishedStubbingExceptions from Mockito.

The helpful error code says that I have an unfinished stub somewhere, but I don't see where it is. The error occurs on the second doReturn() method.

/**
 * Creates a partial mock of a connection that can return an ip address.
 * 
 * @param ipString The IP to return.
 *            
 * @return A PlayerConnection object that can return a Host Address of the ipString but nothing else.
 */
private PlayerConnection newConnection(String ipString)
{
    PlayerConnection playerConnection = mock(PlayerConnection.class);
    InetSocketAddress inetSocketAddress = mock(InetSocketAddress.class);
    InetAddress inetAddress = mock(InetAddress.class);

    doReturn(inetSocketAddress).when(playerConnection).getAddress();
    doReturn(inetAddress).when(inetSocketAddress).getAddress();
    doReturn(ipString).when(inetAddress).getHostAddress();

    return playerConnection;
}
like image 251
J. Doe Avatar asked Mar 01 '17 22:03

J. Doe


1 Answers

Summary: InetSocketAddress.getAddress() is final, as listed in the docs. Due to its clever syntax, Mockito can't easily stub or verify final methods, and can't even tell you when it's trying and failing. In general, don't mock objects you don't control, particularly because of situations like this one.

The helpful error code for UnfinishedStubbingException identifies your problem (see option #2, you naughty developer!):

org.mockito.exceptions.misusing.UnfinishedStubbingException: 
Unfinished stubbing detected here:
-> at 

E.g. thenReturn() may be missing.
Examples of correct stubbing:
    when(mock.isOk()).thenReturn(true);
    when(mock.isOk()).thenThrow(exception);
    doThrow(exception).when(mock).someVoidMethod();
Hints:
 1. missing thenReturn()
 2. you are trying to stub a final method, you naughty developer!
 3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed  

Details: Conventional Mockito mocks work by taking an object and automatically and silently generating a subclass where all the methods are overridden to delegate to Mockito. However, the Java compiler takes advantage of static dispatch for final methods, so calls to final methods don't go directly to Mockito, they go to the real method implementation in InetSocketAddress instead. This subclass instance isn't ever intended to be interacted with, and doesn't have any of its fields set, so it's very easy to get NullPointerException or other behavior—and all of this happens before you interact with Mockito, so you don't even get the benefit of a sane error message when using when(object.methodCall()).thenReturn(), because the unexpected NPE happens while evaluating object.methodCall() before Mockito gets involved at all.

Doing things the other way—doReturn(...).when(object).methodCall()—gives Mockito the chance to see doReturn and when even if the call to methodCall doesn't delegate to Mockito. This gives Mockito the context to say that the stubbing is unfinished, because Mockito can't see the methodCall call.

For more information, see this SO question (Final method mocking) and consider using the recent opt-in mocking of final classes and methods that Mockito introduced in version 2.1.

like image 185
Jeff Bowman Avatar answered Sep 29 '22 05:09

Jeff Bowman