Robin Coe
Robin Coe

Reputation: 750

PowerMock with EasyMock, expectation returns null instead of a mock

Looking for some guidance as to why an expectation returns null instead of the requested mock. The mock in question is a Future and is following the same pattern as other mocks that get returned correctly.

To provide all the information to someone with experience with powermock and easymock, I've included all the code, both code under test and the test code that sets up the mocks and behaviours. The expectation in question is

EasyMock.expect( mockAsyncClient.execute( EasyMock.isA( HttpGet.class ),
EasyMOck.isA( HttpClientContext.class ), isNull() ).andReturn( mockFuture )

which produces a null instead of returning a mocked Future.

Any ideas would be appreciated.

p.s. there is an awful lot of mock setup required to test this function, which I hope doesn't hinder evaluating the problem. Any advice to remove unnecessary mocking infrastructure would be appreciated.

Here's the code under test

    public <T> T getResponse( ResponseHandler<T> responseHandler )
    throws IOException, InterruptedException, ExecutionException
{
    String connectTo = buildUri();

    try( CloseableHttpAsyncClient httpClient =
                                             HttpAsyncClients.custom()
                                                             .setConnectionManager( connManager )
                                                             .build() ) {
        HttpGet request = new HttpGet( connectTo );
        HttpClientContext ctx = HttpClientContext.create();

        addHeaders( request );

        httpClient.start();

        Future<HttpResponse> futureResponse = httpClient.execute( request, ctx, null ); //<-- this line executes using a verified HttpClient mock but returns null

        HttpResponse response = futureResponse.get();

        return responseHandler.handleResponse( response );
    }
}

test code:

    @Test
@PrepareOnlyThisForTest( { HttpAsyncClients.class, HttpAsyncClientBuilder.class } )
public void testGetResponseCallsResponseHandler()
    throws IOException, InterruptedException, ExecutionException
{
    // create mocks to be used when exercising the code under test
    CloseableHttpAsyncClient mockAsyncClient =
                                             EasyMock.createMock( CloseableHttpAsyncClient.class );

    PowerMock.mockStatic( HttpAsyncClients.class );

    HttpAsyncClientBuilder mockClientBuilder =
                                             PowerMock.createMock( HttpAsyncClientBuilder.class );
    HttpAsyncClientBuilder mockClientBuilder2 =
                                              PowerMock.createMock( HttpAsyncClientBuilder.class );

    HttpResponse mockResponse = PowerMock.createMock( HttpResponse.class );
    StatusLine mockStatusLine = PowerMock.createMock( StatusLine.class );
    @SuppressWarnings( "unchecked" )
    Future<HttpResponse> mockFuture = PowerMock.createMock( Future.class );

    // set up expectations that use the mocks
    EasyMock.expect( HttpAsyncClients.custom() ).andReturn( mockClientBuilder );
    EasyMock.expect( mockClientBuilder.setConnectionManager( EasyMock.isA( NHttpClientConnectionManager.class ) ) )
            .andReturn( mockClientBuilder2 );
    EasyMock.expect( mockClientBuilder2.build() ).andReturn( mockAsyncClient );

    mockAsyncClient.start();
    EasyMock.expectLastCall().once();

    EasyMock.expect( mockAsyncClient.execute( EasyMock.isA( HttpGet.class ),
                                              EasyMock.isA( HttpClientContext.class ),
                                              EasyMock.isNull() ) )
            .andReturn( mockFuture );

    EasyMock.expect( mockFuture.get() ).andReturn( mockResponse );

    EasyMock.expect( mockResponse.getStatusLine() ).andReturn( mockStatusLine );
    EasyMock.expect( mockStatusLine.getStatusCode() ).andReturn( 200 );

    mockAsyncClient.close();
    EasyMock.expectLastCall().once();

    PowerMock.replayAll();

    ClientConfig cfg = new ClientConfigBuilder().build();
    RestClient client = new RestClient( cfg );

    int statusCode = client.getResponse( new ResponseHandler<Integer>() {

        @Override
        public Integer handleResponse( HttpResponse response )
            throws ClientProtocolException, IOException
        {
            StatusLine statusLine = response.getStatusLine();
            return statusLine.getStatusCode();
        }

    } );

    PowerMock.verifyAll();

    assertEquals( "status code incorrect", 200, statusCode );
}

Upvotes: 1

Views: 1679

Answers (2)

MatrixManAtYrService
MatrixManAtYrService

Reputation: 9131

I wound up here because I was trying to whenNew(SomeClass.class).withAnyArguments() on a class like:

class SomeClass {
     public SomeClass(String... args) { }
}

Turns out that withAnyArguments() doesn't match varargs. In my case the fix was:

whenNew(SomeClass.class).withArguments(Matchers.<String>anyVararg()).thenReturn(myMock);

Upvotes: 0

Robin Coe
Robin Coe

Reputation: 750

I found the problem and it's one of those problems I'm sure has hit many others...

The mock that was returning a null can be explained by the fact I mistakenly used EasyMock to create the mock and not PowerMock:

CloseableHttpAsyncClient mockAsyncClient =
                                         EasyMock.createMock( CloseableHttpAsyncClient.class );

By changing that line to

CloseableHttpAsyncClient mockAsyncClient =
                                         PowerMock.createMock( CloseableHttpAsyncClient.class );

the test passed.

Reason is that PowerMock owns the mocks it is used to create and can only verify behaviour on those mocks. PowerMock wraps EasyMock and so needs visibility into the mocks under control.

For those affected by this problem, please upvote the question. Not sure why I was down-voted but please make this answer easier to discover.

Thanks, Robin.

Upvotes: 1

Related Questions