priyanka
priyanka

Reputation: 472

How to mock HttpClient using Mockito

This is my Actual class for which i am writing junit. I have HtpClient as private and final.

 public class KMSHttpClientImpl implements KMSHttpClient
 {
/**
 * ObjectMapper Instance.
 */
private final ObjectMapper objectMapper = new ObjectMapper ();

/**
 * KMS ConnectionManager Instance.
 */
private final KMSHttpConnectionManager kmsHttpConnectionManager =
        new KMSHttpConnectionManagerImpl ();

/**
 * HttpClient object.
 */

private final HttpClient httpClient;

/**
 * KMSHttpClient constructor.
 */
public KMSHttpClientImpl ()
{
    // TODO PoolingHttpClientConnectionManager object should be closed after use.
    // TODO This needs to be either singleton or should be kept in static block
    final PoolingHttpClientConnectionManager connectionManager =
            kmsHttpConnectionManager.getConnectionManager();
    httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .build();
}

@Override
public <T> T invokeGETRequest (final String url, final Class<T> clazz)
        throws KMSClientException
{
    final HttpGet httpGet = new HttpGet(url);
    try {
        final HttpResponse response = httpClient.execute(httpGet);
        return objectMapper.readValue(
                response.getEntity().getContent(), clazz);
    } catch (IOException e) {
        throw new KMSClientException("Unable to get the result", e);
    }
}

@Override
public <T> T invokePOSTRequest (final String url, final Object object, final Class<T> clazz)
        throws KMSClientException
{
    final HttpPost httpPost = new HttpPost(url);
    try {
        final HttpResponse response = httpClient.execute(httpPost);
        return objectMapper.readValue(
                response.getEntity().getContent(), clazz);
    } catch (IOException e) {
        throw new KMSClientException("Unable to create the request", e);
    }
}
 }

This is my testclass. I am trying to Mock HttpClient but as it is final i cant mock it. And if i remove final from HttpClient in my KMSHttpClientImpl.java class. I am getting PMd issue saying Private field 'httpClient' could be made final; it is only initialized in the declaration or constructor. What can i do to fix this issue?

public class KMSHttpClientImplTest
{

/**
 * Injecting mocks KMSHttpClientImpl.
 */
@InjectMocks
private KMSHttpClientImpl kmsHttpClientImpl;

/**
 * Mock HttpClient.
 */
@Mock
private HttpClient httpClient;


/**
 * Initial SetUp Method.
 */
@Before
public void setUp ()
{
    initMocks(this);
}

/**
 * Method to test postRequest Method.
 * @throws KMSClientException
 */
@Test
public void testPostRequest () throws KMSClientException
{
    final OrganizationRequest request = getOrganizationRequest();
    final HttpResponse response = prepareResponse(HttpStatus.SC_OK);
    try {
        Mockito.when(httpClient.execute(Mockito.any())).thenReturn(response);
        final OrganizationResponse organizationResponse = kmsHttpClientImpl.invokePOSTRequest(
                ORG_TEST_URL, request, OrganizationResponse.class);
        assertEquals("Id should match", ORG_ID, organizationResponse.getId());
    } catch (IOException e) {
        throw new KMSClientException("Unable to create the request", e);
    }
      }

/**
 * Method to test getRequest Method.
 * @throws KMSClientException
 */
@Test
public void testGetRequest () throws KMSClientException
{
    try {
        final HttpResponse response = prepareResponse(HttpStatus.SC_OK);
        Mockito.when(httpClient.execute(Mockito.any())).thenReturn(response);
        final OrganizationResponse organizationResponse = kmsHttpClientImpl.invokeGETRequest
                (ORG_TEST_URL, OrganizationResponse.class);
        assertEquals("Id should match", ORG_ID, organizationResponse.getId());
    }  catch (IOException e) {
        throw new KMSClientException("Unable to create the request", e);
    }
}

/**
 * Method to organizationRequest Object.
 * @return OrganizationRequest object
 */
public OrganizationRequest getOrganizationRequest ()
{
    return OrganizationRequest.builder().id("test").build();
}

/**
 * Method to getOrganizationResponse String.
 * @return String Object
 */
public String getOrganizationResponse ()
{
    final Map obj=new HashMap();
    obj.put("id", ORG_ID);
    obj.put("uuid", ORG_UUID);
    obj.put("orgKeyId", ORG_KEYID);
    return JSONValue.toJSONString(obj);
}

/**
 * Method to prepare Response.
 * @param expectedResponseStatus
 * @return HttpResponse
 */
private HttpResponse prepareResponse (final int expectedResponseStatus)
{
    final HttpResponse response = new BasicHttpResponse(new BasicStatusLine(
            new ProtocolVersion("HTTP", 1, 1),
            expectedResponseStatus, ""));
    response.setStatusCode(expectedResponseStatus);
    final HttpEntity httpEntity = new StringEntity(getOrganizationResponse(),
            ContentType.APPLICATION_JSON);
    response.setEntity(httpEntity);
    return response;
     }
    }

Upvotes: 0

Views: 6402

Answers (4)

Andy Brown
Andy Brown

Reputation: 12999

Apache HTTP client 5 deprecates the single argument HttpClient.execute() method in favour of the methods that take a ResponseHandler so that the library can ensure that the request is cleaned up.

To mock those calls, do this:

// mock the http client
CloseableHttpClient httpClient = mock(CloseableHttpClient.class);

// mock the response object
CloseableHttpResponse response = mock(CloseableHttpResponse.class);
when(response.getCode()).thenReturn(200);
// [...] add other mocks, e.g. response headers and body entity

// provide the mock response object when your mock HttpClient is executed
// replace any() with more specific matchers if you need to

when(httpClient.execute(any(ClassicHttpRequest.class), any(HttpClientResponseHandler.class))).thenAnswer(invocation ->
  invocation.getArgument(1, HttpClientResponseHandler.class).handleResponse(response));

Upvotes: 1

Acanda
Acanda

Reputation: 682

A simple way to use a mocked HttpClient for your tests is to add a second constructor that takes a HttpClient.

public class KMSHttpClientImpl implements KMSHttpClient
{

  private final HttpClient httpClient;

  public KMSHttpClientImpl ()
  {
    final PoolingHttpClientConnectionManager connectionManager =
            kmsHttpConnectionManager.getConnectionManager();
    httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .build();
  }

  // This constructor is package private instead
  // of public so it is not accidentally used by
  // classes outside of this package. If your test
  // class is not in the same package, then you
  // need to make this a public constructor.
  KMSHttpClientImpl (final HttpClient httpClient)
  {
    this.httpClient = httpClient;
  }

}

You then inject your mocked HttpClient using this constructor and will need neither @InjectMocks nor @Mock in your tests.

@Test
public void testPostRequest () throws KMSClientException
{
  final HttpClient httpClient = Mockito.mock(HttpClient.class);
  final HttpResponse response = prepareResponse(HttpStatus.SC_OK);
  Mockito.when(httpClient.execute(Mockito.any())).thenReturn(response);
  final KMSHttpClientImpl kmsHttpClientImpl = new KMSHttpClientImpl(httpClient);

  // run your test...
}

Upvotes: 0

TrueDub
TrueDub

Reputation: 5070

You can't mock a final instance using plain mocking. You need something like PowerMock.

See the answer to this questionfor implementation.

Upvotes: -1

Shubham Anand
Shubham Anand

Reputation: 53

One of the ways to test a HTTP client code would be to not mock your HTTPClient object, but to create mock responses for the http calls and then let your HPPTClient make calls to those URLs. Take a look at Wiremock. http://wiremock.org/docs/ It helps you create a simple mock server and you can stub responses for your URLs. Then invoke your URLs using your client for the test.

Upvotes: 2

Related Questions