I am trying to write unit test cases for my HTTP Client and would like to use mockito to mock the responses received from the server.
public HttpResponse postRequest(String uri, String body) throws IOException {
HttpResponse response;
String url = baseUrl + uri;
try (CloseableHttpClient httpClient = HttpClientBuilder.create()
.build()) {
HttpPost post = new HttpPost(url);
post.setEntity(new StringEntity(body));
post.setHeader(AUTHORIZATION_HEADER, authorization);
post.setHeader(CONTENTTYPE_HEADER, APPLICATION_JSON);
post.setHeader(ACCEPT_HEADER, APPLICATION_JSON);
response = httpClient.execute(post);
} catch (IOException e) {
System.out.println("Caught an exception" + e.getMessage().toString());
logger.error("Caught an exception" + e.getMessage().toString());
throw e;
}
return response;
}
My test class is as follows. I am unable to figure out how I should send my response body.
public class HTTPRequestTest extends Mockito {
private String body = "{a:b}";
#Test
public void xyz throws Exception {
HttpClient httpClient = mock(HttpClient.class);
HttpPost httpPost = mock(HttpPost.class);
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
when(httpClient.execute(httpPost)).thenReturn(body);
}
}
Using PowerMockito :
First annotate your test class
#RunWith(PowerMockRunner.class)
#PrepareForTest(HttpClientBuilder.class)
then your test method can be something like:
#Test
public void xyz() throws Exception {
HttpClientBuilder mockClientBuilder = PowerMockito.mock(HttpClientBuilder.class);
CloseableHttpClient mockHttpClient = PowerMockito.mock(CloseableHttpClient.class);
CloseableHttpResponse mockResponse = PowerMockito.mock(CloseableHttpResponse.class);
PowerMockito.mockStatic(HttpClientBuilder.class);
PowerMockito.when(HttpClientBuilder.class, "create").thenReturn(mockClientBuilder);
PowerMockito.when(mockClientBuilder.build()).thenReturn(mockHttpClient);
PowerMockito.when(mockHttpClient.execute(any(HttpPost.class))).thenReturn(mockResponse);
HttpResponse response = classUnderTest.postRequest("uri", "body");
//assertResponse
}
The problem is that your HttpClient mock isn't being used in your unit test.
Your postRequest function creates a new HttpClient using this method:
HttpClientBuilder.create().build()
HttpClientBuilder instantiates a new HttpClient, which is a totally separate instance of HttpClient than the mock you create in your unit test. Unfortunately, there isn't an easy way to test your code as written because Mockito can't mock static methods (like HttpClientBuilder.create). See this post for more discussion of this problem and possible workarounds.
To summarize that post, your options are to either rewrite your code so you can inject a mock more easily, or switch to a mocking framework that can mock static methods (like PowerMock).
As suggested in other answers, PowerMock is definitely an option here to mock static methods (in your case HttpClientBuilder.create().build()), however, for your particular instance, you can also resolve the issue by moving the instantiation of the HttpClient out of your doPost method and declare it as instance variable.
#Mock
CloseableHttpClient httpClient = HttpClientBuilder.create().build()
This way when you mock using Mockito, it will be a mocked object. In JUnit5, this can be done by using #Mock above the CloseableHttpClient declaration and then initializing all mocks in the setup method.
Related
I have a problem with my unit test. In my unit test I am getting 401 Unauthorised as response status and I don't know how to solve this problem. This is not a Spring project.
My Test class
#RunWith(MockitoJUnitRunner.class)
public class LTest {
#Test
public void test_retrieve() throws Exceptions{
CloseableHttpClient mockHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockHttpResponse = mock(CloseableHttpResponse.class);
HttpEntity mockEntity = mock(HttpEntity.class);
StatusLine mockStatusLine = mock(StatusLine.class);
when(mockHttpClient.execute(new HttpGet(new URIBuilder(anyString()).build()))).thenReturn(mockHttpResponse);
when(mockHttpResponse.getEntity()).thenReturn(mockEntity);
when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine);
when(mockStatusLine.getStatusCode()).thenReturn(HttpStatus.SC_OK);
Map<String, Employee> map = sample.retrieve();
assertNotNull(map);
assertEquals(1,map.size());
}
source code for the above test case
CloseableHttpClient httpClient = HttpClientUtils.setupClient(HttpClientBuilder.create()).build();
String url = "http://someexample.com";
UriBuilder builder = new URIBuilder(url)
.setParameter("limit",5)
.setParameter("centre",centre);
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(builder.build()));
if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
try{
String entity = EntityUtils.toString(httpResponse.getEntity());
ObjectNode node = new ObjectMapper().readValue(entity,ObjectNode.class);
} catch (IOException e){
e.printStackTrace();
}
}
While running the test case it's showing assertion error because it's going through catch block due below line is throwing 401 instead of 200
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(builder.build()));
Can anyone please help me with the above error I am getting?
In the test, HTTPClient is not mocked and that is the reason for the failure.
To mock the HTTPClient we can follow the below strategy
Extract getHttpClient() in the ClassToBeTested as
public class HttpClientToBeTested {
public Map retrieve() throws URISyntaxException, IOException {
CloseableHttpClient httpClient = getHttpClient();
String url = "http://someexample.com";
URIBuilder builder = new URIBuilder(url)
.setParameter("limit","5")
.setParameter("centre","centre");
CloseableHttpResponse httpResponse = httpClient.execute(new HttpGet(builder.build()));
if(httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
try{
String entity = EntityUtils.toString(httpResponse.getEntity());
//ObjectNode node = new ObjectMapper().readValue(entity,ObjectNode.class);
Map node = new ObjectMapper().readValue(entity, Map.class); // Assume ObjectNode is a custom class, so for demo using Map.
return node;
} catch (IOException e){
e.printStackTrace();
throw e;
}
}
return null;
}
// New extracted method that will be mocked in the test case
protected CloseableHttpClient getHttpClient() {
return HttpClientBuilder.create().build();
}
}
Next in the test class, we can inject the mock by subclassing the ClassToBeTested in an anonymous class as follows.
#Test
public void test_retrieve() throws Exception {
CloseableHttpClient mockHttpClient = mock(CloseableHttpClient.class);
CloseableHttpResponse mockHttpResponse = mock(CloseableHttpResponse.class);
//HttpEntity mockEntity = mock(HttpEntity.class); Not required since we will pass actual entity
StatusLine mockStatusLine = mock(StatusLine.class);
when(mockHttpClient.execute(new HttpGet(new URIBuilder(anyString()).build()))).thenReturn(mockHttpResponse);
when(mockHttpResponse.getEntity()).thenReturn(new StringEntity("{\"key\":\"value\"}")); // Important: Pass your actual response as string here.
when(mockHttpResponse.getStatusLine()).thenReturn(mockStatusLine);
when(mockStatusLine.getStatusCode()).thenReturn(HttpStatus.SC_OK);
// Code to mock the http client
HttpClientToBeTested sample = new HttpClientToBeTested() {
#Override
protected CloseableHttpClient getHttpClient() {
return mockHttpClient;
}
};
Map map = sample.retrieve();
assertNotNull(map);
assertEquals(1,map.size());
}
UPDATE: After mocking the httpClient, httpClient.execute() should return SC_OK. However, post that deserializing the response will fail since the mock HttpEntity will return null. To avoid it, we will rather send a StringEntity. Updated the actual and test class with the details.
I have multiple HttpPost requests like the one shown below:
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost(searchURL);
httpPost.setEntity(...);
ResponseHandler<String> responseHandler = response -> {
HttpEntity httpEntity = response.getEntity();
return httpEntity != null ? EntityUtils.toString(httpEntity) : null;
};
String responseBody = httpclient.execute(httpPost, responseHandler);
} catch()...
For testing these classes, I am mocking the HttpPost requests as under:
when(HttpClients.createDefault()).thenReturn(client);
when(response.getEntity()).thenReturn(entity);
whenNew(HttpPost.class).withArguments(url).thenReturn(httpPostSearchOrg);
when(client.execute(same(httpPostSearchOrg), any(ResponseHandler.class)))
.thenReturn(JSON_STRING);
Now with this test approach, I can mock only one response for POST call to the url.
Is it possible to mock multiple responses based on POST request body(ie. based on the request entity)?
You can probably use an ArgumentCaptor and an Answer:
ArgumentCaptor<HttpEntity> requestEntity = ArgumentCaptor.forClass(HttpEntity.class);
Mockito.doNothing().when(httpPostSearchOrg).setEntity(requestEntity.capture());
when(client.execute(same(httpPostSearchOrg), any(ResponseHandler.class))).thenAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
if (matchesEntityToReturnResponse1(requestEntity.getValue())) {
return "RESPONSE1";
} else {
return "RESPONSE2";
}
}
});
This question already has answers here:
What is the difference between CloseableHttpClient and HttpClient in Apache HttpClient API?
(8 answers)
Closed 2 years ago.
I'm using CloseableHttpResponse (from apache-httpclient-4.5.3) and I'm not sure I'm using it right, I saw an answer with no votes to use EntityUtils.consume on finally:
CloseableHttpResponse response1 = httpclient.execute(httpGet);
try {
System.out.println(response1.getStatusLine());
} finally {
EntityUtils.consume(response1.getEntity());
CloseableHttpClient is abstract and has no close method to call although in this answer it's used:
CloseableHttpResponse response = httpclient.execute(httpget);
try {
//do something
} finally {
response.close();
}
Currently I'm using try with resources for CloseableHttpClient and CloseableHttpResponse inside of send method.
Am I not missing any resource open or using it in a wrong way?
private CloseableHttpResponse send()
throws URISyntaxException, UnsupportedEncodingException, IOException, ClientProtocolException {
URIBuilder uriBuilder = new URIBuilder(BASE_URL);
HttpHost target = new HttpHost(uriBuilder.getHost(), uriBuilder.getPort(), uriBuilder.getScheme());
HttpPost post = new HttpPost(uriBuilder.build());
try (CloseableHttpClient httpClient = HttpClients.custom().build(); CloseableHttpResponse response = httpClient.execute(target, post)) {
return response;
}
It has been explained in detail in the docs here.
Quoting the pseudo code from the docs here's a typical way to allocate/deallocate an instance of CloseableHttpClient:
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
<...>
}
The same applies to CloseableHttpResponse :
try (CloseableHttpResponse response = httpclient.execute(httpget)) {
<...>
}
Now, about the close method in CloseableHttpClient. CloseableHttpClient is an abstract class that implements Closeable interface. That is, although it doesn't have a close method itself the classes that extend it are required to implement the close method. One class is InternalHttpClient. You can check the source code for the details.
Before Java7, explicit close would be required:
CloseableHttpClient httpclient = HttpClients.createDefault();
try {
<...>
} finally {
httpclient.close();
}
CloseableHttpResponse response = httpclient.execute(httpget);
try {
<...>
} finally {
response.close();
}
You can avoid the finally by using the try(resource)
try (CloseableHttpResponse response = httpclient.execute(httpGet)) {
... }
I know how to mock a default HttpClient, but how do I mock the latest (v4.4) HttpClient that is created using a PoolingHttpClientConnectionManager with Mockito?
My code looks like this:
PoolingHttpClientConnectionManager mgr = new PoolingHttpClientConnectionManager();
...
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(mgr).build();
HttpResponse response = httpClient.execute(request);
... // here I want to substitute a mocked HttpResponse
It is likely to be easier to mock out HttpRequestExecutor than HttpClientConnection. You would still need to provide a no-op implementation of HttpClientConnectionManager in order to prevent HttpClient from creating and connecting sockets
HttpRequestExecutor requestExecutor = Mockito.mock(HttpRequestExecutor.class);
Mockito.when(requestExecutor.execute(
Mockito.<HttpRequest>any(),
Mockito.<HttpClientConnection>any(),
Mockito.<HttpContext>any())).thenReturn(new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "Hah"));
HttpClientConnectionManager cm = Mockito.mock(HttpClientConnectionManager.class);
HttpClientConnection conn = Mockito.mock(HttpClientConnection.class);
ConnectionRequest connRequest = Mockito.mock(ConnectionRequest.class);
Mockito.when(cm.requestConnection(
Mockito.<HttpRoute>any(),
Mockito.any())).thenReturn(connRequest);
Mockito.when(connRequest.get(
Mockito.anyLong(),
Mockito.<TimeUnit>any())).thenReturn(conn);
CloseableHttpClient client = HttpClients.custom()
.setRequestExecutor(requestExecutor)
.setConnectionManager(cm)
.build();
CloseableHttpResponse response = client.execute(new HttpGet("http://pampa/"));
try {
System.out.println(response.getStatusLine());
} finally {
response.close();
}
Just realized that PoolingHttpClientConnectionManager is the default connection manager used by HttpClient built with HttpClients.custom().build() (at least in v4.4), so I just went back to mocking the HttpClient.
protected HttpClient buildHttpClient() {
return HttpClients.custom()./* other config */.build();
}
unit test code:
#Mock
HttpClient mockClient;
#Mock
HttpResponse mockResponse;
#Spy
MyClass myclass = new MyClass();
. . .
#Test
public void myTestCase() {
. . .
when(myclass.buildHttpClient()).thenReturn(mockClient);
when(mockClient.execute(requestCaptor.capture())).thenReturn(mockResponse);
. . .
here are two little helper methods I have made for downloading files. I have had to mix and match different tutorials of the web to get what I have here.
Now is there anything that I have done blatantly wrong here?
public static InputStream simplePostRequest(URL url, List<NameValuePair> postData) throws ClientProtocolException, IOException {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpPost postMethod=new HttpPost(url.toExternalForm());
postMethod.setEntity(new UrlEncodedFormEntity(postData, HTTP.UTF_8));
HttpResponse response = httpclient.execute(postMethod);
HttpEntity entity = response.getEntity();
return entity.getContent();
}
public static InputStream simpleGetRequest(URL url, List<NameValuePair> queryString) throws ClientProtocolException, IOException {
Uri.Builder uri = new Uri.Builder();
uri.path(url.getPath());
for(NameValuePair nvp: queryString) {
uri.appendQueryParameter(nvp.getName(), nvp.getValue());
}
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpHost host = new HttpHost(url.getHost());
HttpResponse response = httpClient.execute(host, new HttpGet(uri.build().toString()));
HttpEntity entity = response.getEntity();
return entity.getContent();
}
I wouldn't expect a huge response to a such a vague question. Why not write a pair of unit tests to try your code out instead?
Anyway, the one thing that stands out for me based on my experience with HttpClient is that if subject to heavy load (large number of concurrent threads), your code seems unsafe - there seems to be no upper bound to the number of concurrent connections that could get created.
If you think this might be relevant in your case, you could try something like this:
class X {
private static final HttpClient httpClient;
static {
SchemeRegistry defaultRegistery = new DefaultHttpClient().getConnectionManager()
.getSchemeRegistry();
ThreadSafeClientConnManager connMgr = new ThreadSafeClientConnManager(defaultRegistery);
connMgr.setMaxTotalConnections(10);
connMgr.setDefaultMaxPerRoute(10);
httpClient = new DefaultHttpClient(connMgr);
httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 30000);
}
public static InputStream simpleGetRequest(URL url, List<NameValuePair> queryString) throws ClientProtocolException, IOException {...}
public static InputStream simpleGetRequest(URL url, List<NameValuePair> queryString) throws ClientProtocolException, IOException {...}
}
... and just use the static httpClient in your methods instead of instantiating a new one every time.
I copied and modified this from some code I've written before, but don't consider this necessarily correct. The unit tests will be your friends here.
EDIT: With regards to your comment about mixing URL and the HttpClient library class NameValuePair (is this your concern?), why not just a Map<String, String> in the method signatures?