While running test case ClientHttpResponse is throwing 401 error - java

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.

Related

Java , PowerMock -- Mock Response based on HttpPost request body

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";
}
}
});

Retrofit: Making Web Requests to Internal APIs

I want to make a request to my organisation api's. The request contains Headers, UserName, Password, & Cookie for session management.
Below is the actual code (in HttpClient) which I want to rewrite using Retrofit. I have heard that HttpClient libraries have been deprecated or someting so have opted Retrofit. I expect the response with 200 status code.
public static CookieStore cookingStore = new BasicCookieStore();
public static HttpContext context = new BasicHttpContext();
public String getAuth(String login,String password) {
String resp = null;
try {
String url = DOMAIN+"myxyzapi/myanything";
context.setAttribute(HttpClientContext.COOKIE_STORE, cookingStore);
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(url);
String log = URLEncoder.encode(login, "UTF-8");
String pass = URLEncoder.encode(password, "UTF-8");
String json = "username="+log+"&password="+pass+"&maintain=true&finish=Go";
StringEntity entity = new StringEntity(json);
post.setEntity(entity);
post.addHeader("Content-Type", "application/x-www-form-urlencoded");
HttpResponse response = client.execute(post,context);
resp = EntityUtils.toString(response.getEntity());
accountPoller();
} catch(Exception a) {
log.info("Exception in authentication api:"+a.getMessage().toString());
}
return resp;
}
Below is my code where I can't figure out how to pass the context with request. HttpResponse response = client.execute(post,**context**); using retrofit.
I don't even know if I have made my retrofit request right.
try {
String log = URLEncoder.encode(login, "UTF-8");
String pass = URLEncoder.encode(password, "UTF-8");
RequestBody formBody = new FormBody.Builder()
.add("username=", xyz)
.add("password=", mypass)
.add("&maintain=", "true")
.add("finish=", "Go")
.build();
String url = www.xyz.com+"myxyzapi/myanything";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).post(formBody).addHeader("Content-Type", "application/x-www-form-urlencoded").build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){
final String myresp = response.body().string();
}
}
});
} catch(Exception a) {
a.getMessage();
}
You have to catch exception and use this class.
retrofit2.HttpException
retrofit2
Class HttpException
int
code()
HTTP status code.
String
message()
HTTP status message.
Response
response()
The full HTTP response.

How is this the wrong checked Exception in JUnit?

I have this method:
public void updateService(JSONObject json, String url) throws IOException {
PrintStream log = this.getLogger();
CloseableHttpClient httpClient = null;
httpClient = this.getCloseableHttpClient();
log.println("Sending data to " + url);
HttpPut request = new HttpPut(url);
StringEntity params = new StringEntity(json.toString());
request.addHeader("content-type", "application/json");
request.setEntity(params);
httpClient.execute(request);
log.println("Sending report succeeded");
httpClient.close();
}
My test then does this:
#Test
public void updateServiceCloseException() {
RegistryTask task = new RegistryTask(false, null);
RegistryTask spy = spy(task);
CloseableHttpClient client = HttpClientBuilder.create().build();
CloseableHttpClient clientSpy = spy(client);
String url = "http://www.example.com/api/service/testo";
String message = "Execute failure";
JSONObject json = new JSONObject();
json.put("name", "Bob");
json.put("key", "testo");
try {
doReturn(clientSpy).when(spy).getCloseableHttpClient();
// Make sure no http request is actually sent
CloseableHttpResponse response = mock(CloseableHttpResponse.class);
IOException exception = new IOException(message);
doReturn(response).when(clientSpy).execute(any(HttpPut.class));
doThrow(exception).when(clientSpy).close();
spy.updateService(json, url);
} catch (IOException e) {
failWithTrace(e);
// assertEquals(message, e.getMessage());
return;
} catch (Exception e) {
failWithTrace(e);
// assertEquals(message, e.getMessage());
return;
}
fail("Exception not thrown");
}
For some reason doThrow(exception).when(clientSpy).close(); is saying Checked exception is invalid for this method!. But considering my method has throws IOException and close itself throws IOException, I am completely confused about getting this JUnit exception.
Update
I tried updating the doReturn to when(clientSpy.execute(any(HttpPut.class))).thenReturn(response);. The new exception is java.lang.AssertionError: java.lang.IllegalArgumentException: HTTP request may not be null. Not sure why using any would count as null in this case. This question actually shows I should use doReturn since it always work vs thenReturn not working in all cases... Mockito - difference between doReturn() and when()
I changed from spy to full mock with CloseableHttpClient clientSpy = mock(CloseableHttpClient.class);. I know this removes all methods from running, but I was already using spy to override the two methods I was hitting. While this was the solution, I'd love a proper explanation as the answer for future readers.

Writing Mock class for java http client

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.

Getting data with Httpclient and displaying it with JSON

I have to write a code that retrieves specific information (not all of it) from url.com/info/{CODE} and uses json to display it in a server I have up.
This is my code up until now:
A class to get the info
#RequestMapping("/info")
public class Controller {
public void httpGET() throws ClientProtocolException, IOException {
String url = "Getfromhere.com/";
CloseableHttpClient client = HttpClients.createDefault();
HttpGet request = new HttpGet(url);
CloseableHttpResponse response = client.execute(request);
}
and a class that should return the data depending on the code inserted in the url by the user
#RequestMapping(value = "/{iataCode}", method = RequestMethod.GET)
#ResponseBody
public CloseableHttpResponse generate(#PathVariable String iataCode) {
;
return response;
}
How I can implement json for the return?.
To begin with, you must configure Spring to use Jackson or some other API to convert all your responses to json.
If the data you are retrieving is already in json format, you can return it as String.
Your big mistake: right now you are returning an object of type CloseableHttpResponse. Change return type of generate() from CloseableHttpResponse to String and return a string.
CloseableHttpResponse response = client.execute(request);
String res = null;
HttpEntity entity = response.getEntity();
if (entity != null) {
InputStream instream = entity.getContent();
byte[] bytes = IOUtils.toByteArray(instream);
res = new String(bytes, "UTF-8");
instream.close();
}
return res;

Categories