Unable to cover catch block code coverage - java

I am not able to cover below HttpClientErrorException catch block in Junit code coverage. How can I do this?
code
#Autowired
private ExceptionHandlerService errorHandler;
public CartResponse callUpdateCart(AddToCartRequest request) {
String url = Utils.formatHttpUrl(cartbaseUrl, CART_UPDATE_CART);
try {
HttpEntity<?> entity = new HttpEntity<>(request);
JsonNode jsonNode = restTemplate.postForObject(url, entity, JsonNode.class);
if (jsonNode.has(Constants.CONTENT) && !jsonNode.path(Constants.CONTENT).path(Constants.PAYLOAD).isMissingNode()) {
jsonNode = jsonNode.path(Constants.PAYLOAD).get(Constants.PAYLOAD);
} else {
errorHandler.error(ErrorMessages.EPO_VALIDATEQ_ERROR_08, jsonNode);
}
return JsonService.getObjectFromJson(jsonNode, CartResponse.class);
} catch (HttpClientErrorException e) {
errorHandler.error(ErrorMessages.EPO_VALIDATEQ_ERROR_08, e.getResponseBodyAsString());
return null;
} catch (HttpServerErrorException e) {
throw new ServiceException(ErrorMessages.EPO_SYSTEM_ERROR, e.getMessage(), url);
}
}
ExceptionHandlerService
#Override
public void error(ResolvableErrorEnum error, String responseBody) {
JsonNode response = JsonService.getObjectFromJson(responseBody, JsonNode.class);
if (null != response && null != response.get(Constants.ERROR)) {
ServiceError serviceError = JsonService.getObjectFromJsonNode(response.get(Constants.ERROR), ServiceError.class);
error(error, serviceError.getErrorId(), serviceError.getMessage());
}
throw new ServiceException(error);
}
junit
#Test(expected = ServiceException.class)
public void test_callUpdateCart_Exception() throws IOException {
AddToCartRequest req = createAddToCartRequest();
String responseBodyStr = "{\"error\":{\"errorId\":\"Service-I-1003\",\"message\":\"Error returned from downstream system.\",\"traceCode\":\"CART;400\",\"details\":[{\"code\":\"400\",\"message\":\"400 Bad Request\"},{\"code\":\"DTV_CAT_ERR_002\",\"message\":\"Error in getting response from catalog.\",\"traceCode\":\"CART;400\"}]}}\r\n";
byte[] body = responseBodyStr.getBytes(StandardCharsets.UTF_8);
HttpClientErrorException e = new HttpClientErrorException(HttpStatus.BAD_REQUEST, "BAD REQUEST", body,
StandardCharsets.UTF_8);
when(restTemplate.postForObject(Mockito.anyString(), Mockito.<HttpEntity<?>>any(), Mockito.eq(JsonNode.class)))
.thenThrow(e);
client.callUpdateCart(req);
}
error

It seems like the mockito.when() is not working properly, since it looks like it is not throwing the exception you are asking it to throw. I had similar issues and tinkering with mockito matchers usually fixes them.
You can check this page for a bit more information regarding "expanding" or restricting your argument matchers.
I think the solution bellow will work:
when(restTemplate.postForObject(Mockito.anyString(), Mockito.any(HttpEntity.class), Mockito.any(JsonNode.class)))
.thenThrow(e);

Related

JUnit test not catching exception

UPDATE: Here's the full test:
#Test(expected = NullPointerException.class)
public void testMissingData() throws Exception{
Resource<ObjectDataModel, Content, Status> resource = builder.build(content, argA1Response,
argA2Response, objFilterParam, argA3Response);}
And here's the build method:
public Resource<ObjectDataModel, Content, Status> build(Content argContent,
ResponseA1 argA1Response,
ResponseA2 argA2Response, String argObjectTypeFilter,
ResponseA3 argA3Response) {
try {
viewDataModel.setObjectType(this.buildObjectType(filteredObjectType,
argA1Response.getData().getDataObject().getCategories().get(0).getObjectTypes().get(0)));
}
catch (Exception e) {
String msg = "Exception occoured while buildng the Object Data Model";
LOG.error(msg, e);
}
// we have the required information gathered to return
return Resource.okFromDataAndContent(viewDataModel, argContent);
}
And here's the buildObjectType() method:
private ObjectType buildObjectType(ObjectTypes argA1ProductType,
PendingObjectTypes argA2ProductType) {
ProductType objectType = new ObjectType();
List<Plan> plans = argA1ObjectType.getPlan();
List<PendingObjectSummary> objPlans = argA1ObjectType.getData();
if (objectType.getData() == null) {
objectType.setData(new ArrayList<>());
}
PendingObjectSummary tempPlan = null;
for (Plan currPlan : plans) {
tempPlan = plans.stream()
.filter(plan -> plan.getObjId().equals(currPlan.getObjId()))
.findFirst()
.orElseThrow(NullPointerException::new);
}
return objectType;
}
I'm using an Optional to test for null and I can confirm that the exception is being thrown -- but JUnit isn't catching it. Here's the test case:
#Test(expected = NullPointerException.class)
public void testMissingData() throws Exception{
Object<> response = fixture.create();
assertNotNull(response);
assertNotNull(response.getData());
assertNull(resource.getData().getObjectType());
}
In my create method I'm simply iterating over a bunch of objects to try and find one that matches my ID; if not found then throw a NullPointerException:
for (Object currObj : objects) {
tempObj = myOtherCollection.stream()
.filter(obj -> obj.getId().equals(currObj.getId()))
.findFirst()
.orElseThrow(NullPointerException::new);
}
The JUnit output clearly isn't catching the exception - here's the output:
java.lang.AssertionError: Expected exception: java.lang.NullPointerException
And my tomcat logs are definitely throwing the exception here:
18:48:30.015 [main] ERROR com.myCompany.src.ModelBuilder - Exception occoured while buildng the Data Model
java.lang.NullPointerException: null
at java.util.Optional.orElseThrow(Optional.java:290)
The only issue I can see is that maybe where I assign tempObj that the code is wrong. Am I missing anything obvious? Thanks for any helpful tips.
You are catching the nullpointer exception so the exception is not propagated to your test.
see
try {
viewDataModel.setObjectType(this.buildObjectType(filteredObjectType,
argA1Response.getData().getDataObject().getCategories().get(0).getObjectTypes().get(0)));
}
catch (Exception e) {
String msg = "Exception occoured while buildng the Object Data Model";
LOG.error(msg, e);
}
If you want to test for an exception you could throw an exception in your error handling (for example a custom ObjectCreationExcepion) and assert that that one is thrown, like
try {
viewDataModel.setObjectType(this.buildObjectType(filteredObjectType,
argA1Response.getData().getDataObject().getCategories().get(0).getObjectTypes().get(0)));
}
catch (Exception e) {
String msg = "Exception occoured while buildng the Object Data Model";
LOG.error(msg, e);
throw new ObjectCreationException(msg);
}
and in your test
#Test(expected = ObjectCreationException.class)
public void testMissingData() throws Exception{
Object<> response = fixture.create();
}
#Test(expected = ObjectCreationException.class) only handles exceptions that are not handled within the tested code OR the test itself.
So what you could do is
public Resource<ObjectDataModel, Content, Status> build(Content argContent,
ResponseA1 argA1Response,
ResponseA2 argA2Response, String argObjectTypeFilter,
ResponseA3 argA3Response) throws NullPointerExceptions // << notice thrwoing declatration
{ // do some stuf}
and then in test you can handle it like you where trying by
public void testMissingData() throws Exception{
Resource<ObjectDataModel, Content, Status> resource = builder.build(content, argA1Response,
argA2Response, objFilterParam, argA3Response);
}

How can I simplify this parsing method?

I am writing code to unmarshal XML from a file. I don't know up front which schema the XML is based on so I try to unmarshal it with several schemas in the form of different Jaxb2Marshaller instances.
The method needs to:
attempt to unmarshal the XML with each marshaller
If this succeeds, return the resulting object
If it fails, try the next marshaller
If all marshallers fail, throw an exception with the last error message
Here is the current code:
private Object getObject(final byte[] data) throws MyException {
String lastErrorMessage = "";
for (final Jaxb2Marshaller marshaller : this.marshallers) {
try {
return marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data)));
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
lastErrorMessage = e.getMessage();
}
}
throw new MyException(lastErrorMessage);
}
I feel this method does too many things at different levels of abstraction:
iterate over marshallers
apply a marshaller
return result
catch exceptions
throw exception
But I don't see a way to simplify it. The try-catch block is needed for every marshaller (because I should catch and ignore these XmlMappingExceptions except the last one). That block either returns a result object, or the lastErrorMessage, which is needed below the iteration to throw the MyException.
The only solution I can think of is to create some contrived Result class which contains either the result object or the error message but that feels cludgy. Any other insights?
I would like methods with a granularity like these:
private Object getObject(byte[] data) throws MyException {
Result result;
for (Jaxb2Marshaller marshaller : this.marshallers) {
result = getObject(marshaller, data);
}
return handleError(result);
}
private Result getObject(Jaxb2Marshaller marshaller, byte[] data) {
try {
return Result.value(marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data))));
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
return Result.error(e.getMessage());
}
}
private Object handleError(Result result) {
if (result.isError()) {
throw new MyException(result.errroMessage);
}
else {
return result.value;
}
}
But the additional Result class is verbose and cludgy:
private class Result {
String errorMessage;
Object value;
static Result error(String errorMessage) {
Result result = new Result();
result.errorMessage = errorMessage;
return result;
}
static Result value(Object value) {
Result result = new Result();
result.value = value;
return result;
}
boolean isError() {
return errorMessage != null;
}
}
How about this?
public class MultiUnmarshaller {
private final List<Jaxb2Marshaller> marshallers;
private Object value;
private String error;
public MultiUnmarshaller(List<Jaxb2Marshaller> marshallers) {
this.marshallers = marshallers;
}
private void init() {
error = "No marshallers available";
value = null;
}
public Object getObject(byte[] data) throws MyException {
init();
Iterator<Jaxb2Marshaller> it = marshallers.iterator();
while(it.hasNext() && errorMessage != null) {
unmarshalObject(marshaller, data);
}
return produceResult();
}
private void unmarshalObject(Jaxb2Marshaller marshaller, byte[] data) {
try {
value = marshaller.unmarshal(new StreamSource(new ByteArrayInputStream(data)));
error = null;
} catch (final XmlMappingException e) {
LOGGER.warn("Invalid XML", e);
error = e.getMessage();
}
}
private Object produceResult() {
if (error == null) {
return value;
}
else {
throw new MyException(error);
}
}
}

How Receieve Validation Errors In Java Android Spring

I am sending API requests to a backend API using Spring in Android (Java). My question is how to receive validation errors to the error handler at ex 400 bad request response. Here is my code:
class RestTask extends AsyncTask<String,Void,ResponseEntity<ExpectedReturn>>
{
protected ResponseEntity<ExpectedReturn> doInBackground(String... uri)
{
try{
final String url = uri[0];
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(subscriber.getErrorHandler());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
// set authentication tokens:
ResponseEntity<ExpectedReturn> response = restTemplate.exchange(url,callMethod,httpEntity, expectedReturnClass);
return response;
}catch(Exception e)
{
System.out.println(e.getMessage());
}
return null;
}
#Override
protected void onPostExecute(ResponseEntity<ExpectedReturn> result) {
if(result !=null && result.getBody() !=null)
{
subscriber.getSubscriber().onComplete(result.getBody(),result.getStatusCode());
}
}
}
My question is, if the post data fails validation (is incorrect), the API will return as JSON error object with errors, ex:
In case of a validation error, the error handler is called with a ClientHttpResponse object as a parameter. Calling the response.getBody() returns an InputStream. My question is, is there any way of receiving an object mapped from the JSON error response (as shown above) to the error handler, or perhaps converting the input stream to something readable (like a hashmap) so I can display the errors returned by the API (ex: "Name is required" etc...)?
I've tested your code and in case of a 400 bad request the catch block receives an instance of HttpClientErrorException which has a method to get the error body as String:
private class HttpRequestTask extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
try {
final String url = "https://reqres.in/api/login";
RestTemplate restTemplate = new RestTemplate();
//Same result with restTemplate.exchange() too
return restTemplate.postForObject(url, "{\n" +
" \"email\": \"peter#klaven\"\n" +
"}", String.class);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
if (e instanceof HttpClientErrorException) {
String responseBodyAsString = ((HttpClientErrorException) e).getResponseBodyAsString();
Log.e(TAG, "Validation error" + responseBodyAsString);
//You can parse this with gson or jackson here
return responseBodyAsString;
}
}
return null;
}
#Override
protected void onPostExecute(String result) {
Log.d(TAG, "onPostExecute() called with: result = [" + result + "]");
}
}
Which prints in:
W/RestTemplate: POST request for "https://reqres.in/api/login" resulted in
400 (Bad Request); invoking error handler
E/MainActivity: 400 Bad Request
E/MainActivity: Validation error{"error":"Missing email or username"}
D/MainActivity: onPostExecute() called with: result = [{"error":"Missing email or username"}]
If you want to use the none default error handler and set your custom error handler you can get the error message as string this way:
restTemplate.setErrorHandler(new ResponseErrorHandler() {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return response.getStatusCode().is4xxClientError();
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
String errorResponse = new String(getResponseBody(response), getCharset(response).name());
Log.e(TAG, "handleError: called with: " + errorResponse);
}
});
private byte[] getResponseBody(ClientHttpResponse response) {
try {
InputStream responseBody = response.getBody();
if (responseBody != null) {
return FileCopyUtils.copyToByteArray(responseBody);
}
} catch (IOException ex) {
// ignore
}
return new byte[0];
}
private Charset getCharset(ClientHttpResponse response) {
HttpHeaders headers = response.getHeaders();
MediaType contentType = headers.getContentType();
return contentType != null ? contentType.getCharSet() : Charset.defaultCharset();
}
Then you can use Jackson or Gson to parse the error response as below:
new Gson().fromJson(responseBodyAsString, ExpectedResponse.class);
Note I've just did the same thing as implemented in DefaultResponseErrorHandler
Edit:
The whole AsyncTask and Spring Android APIs are so outdated, Here is the same example with Retrofit:
api.login(new BodyModel("peter#klaven"))
.enqueue(new Callback<ExpectedModel>() {
#Override
public void onResponse(#NonNull Call<ExpectedModel> call, #NonNull Response<ExpectedModel> response) {
if (response.isSuccessful()) {
//Do what you got to do
} else {
Converter<ResponseBody, ErrorModel> converter = MainActivity.this.retrofit.responseBodyConverter(ErrorModel.class, new Annotation[0]);
ErrorModel errorModel = null;
try {
errorModel = converter.convert(response.errorBody());
Toast.makeText(MainActivity.this, errorModel.toString(), Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(#NonNull Call<ExpectedModel> call, #NonNull Throwable t) {
t.printStackTrace();
}
})
You can find the full gist in my github repo

Spring HttpServerErrorException custom response body not being serialized

I have a Controller like this example:
#RestController
#RequestMapping(value = "/risk", produces = {MediaType.APPLICATION_JSON_VALUE})
public class CalculationController {
#RequestMapping(value = "/calculate", method = RequestMethod.POST)
public CalculationResult calculate(InputFields i) {
try {
return calcService.calculate(i);
} catch (CustomException custEx) {
throw new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR,
null,
null,
getReportLogAsBytes(custEx), //for some reason not working when serialized in Json
Charset.defaultCharset());
}
}
private byte[] getReportLogAsBytes(CustomException e) {
try {
return objectMapper.writeValueAsBytes(e.getReportLog()); //
} catch (JsonProcessingException e1) {
throw new RuntimeException("Unable to serialize Simulation report log to bytes ", e1);
}
}
class CustomException extends Exception {
private List<String> reportLog;
public CustomException(List<String> reportLog) {
super();
this.setReportLog(reportLog);
}
public List<String> getReportLog() {
return reportLog;
}
public void setReportLog(List<String> reportLog) {
this.reportLog = reportLog;
}
}
}
When posting the inputs to the controller and a CustomException occurs, I instantiate HttpServerErrorException using the constructor that accepts a byte array for responseBody. I basically give it a list of String error messages converted to byte array.
The problem is the response body still does not show the list of errors messages I passed to it. I tried looking for examples on using HttpServerErrorException with response body but cannot seem to find any... Any help would be greatly appreciated.
You throw your HttpServerErrorException but don't handle it in the proper way.
Read this: https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc

Testing methods using JUnit

I am new to JUnit and I have to test a method using JUnit api. One method internall calls another. My test case goes inside the method but while catchign the exception it fails.
Method under test is
public void checkANDCondition( Map<String, Message> messagesMap ) throws EISClientException
{
List<String> codes = getMessageCodes();
if(isAllReturnedMessagesContainCodes(codes, messagesMap))
{
StringBuffer buff = new StringBuffer("All of the specified message codes matched returned errors.");
for(String code: codes )
{
Message message = messagesMap.get(code);
buff.append(message.getMessageCode() + ": " + message.getMessageType() + ": " + message.getMessageText() + " ");
}
throw new EISClientException(buff.toString());
}
}
public boolean isAllReturnedMessagesContainCodes(List<String> codes, Map<String, Message> messagesMap)
{
if(codes!=null)
{
for(String code: codes)
{
if(!messagesMap.containsKey(code))
{
return false;
}
}
}
return true;
}
What I have done so far is
#Test
public void testPostProcess() throws Exception {
clientResponse = mock(ClientResponse.class);
MessageToExceptionPostProcessFilter postProcessFilter = new MessageToExceptionPostProcessFilter();
RetrieveBillingServiceResponse serviceResponse = new RetrieveBillingServiceResponse();caughtException = false;
try {
postProcessFilter.setCondition(ConditionOperator.AND);
List<String> messagesCodes = new ArrayList<String>();
messagesCodes.add("200");
messagesCodes.add("400");
Message message = new Message();
message.setMessageCode("200");
message.setMessageType(MessageTypeEnum.MESSAGE_TYPE_INFO);
message.setMessageText("Service completed successfully");
serviceResponse.setMessages(Arrays.asList(message));
postProcessFilter.setMessageCodes(messagesCodes);
serviceResponse = postProcessFilter.postProcess(serviceResponse, clientResponse);
assertNotNull(serviceResponse.getMessages());
} catch (EISClientException ex) {
caughtException = true;
assertEquals("All of the specified message codes matched returned errors.", ex.getMessage());
}
assertTrue(caughtException);
}
How can I make it pass?
Thanks
#Test(expected = EISCLientException.class)
public void testPostProcess() throws Exception {
...
serviceResponse.getMessages();
fail("Shouldn't reach this point");
}
That way you don't need to catch, with expected if it does not get throw a EISClientException it will fail.
edit: There are two times I can think of where you wouldn't want to use this.
1) You are mocking exceptions that are thrown mock(exception.class);
this i believe then throws some Mockito excpetion and it will not match the expected exception.
2) You are wrapping caught exceptions in your code, and throwing a generic exception. Example of code:
try {
} catch (FileParseException e){
throw new (ProjectFailingException(e, "file is bad");
}
if you have multiple catches and are wrapping them as ProjectFailingExceptions then you may want to catch in the test like this...
#Test ( expected = FileParseException.class)
public void testProcess() {
try {
...
} catch (ProjectFailingException e){
throw e.getCause();
}
Then the proper exception is thrown and you can make sure that process isn't throwing an exception from a a different catch.

Categories