I am writing a test case for the Rest Controller for an error condition and when I set expected exception to Exception.class the test runs with no errors. When I change the expected exception to CustomException.class, the test fails with assertion error
Here is my Controller class:
#RestController
public class CustomController<CustomFieldRequest customFieldRequest, CustomField customField> {
#PutMapping(path = "/{customFieldId}",
consumes = MediaType.APPLICATION_JSON_VALUE, produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_ATOM_XML_VALUE})
public CustomField updateCustomField(#PathVariable String customFieldId, #RequestBody CustomFieldRequest customFieldRequest) throws CustomException {
customFieldRequest.setAuthorId(getUser());
customFieldRequest.setId(customFieldId);
return CustomService.updateCustomeField(customFieldRequest);
}
}
Here is my service class:
#Service
public class CustomServiceImpl implements CustomService {
#Override
public CustomField updateCustomeField(CustomFieldRequest customFieldRequest) throws CustomException {
}
}
Here is my custom exception class:
public class CustomException extends Exception {
public CustomException(final String message) {
super(message);
}
public CustomException(final String message, final Throwable cause) {
super(message, cause);
}
}
Here is my test class:
public class CustomControllerTest {
private CustomService customService = Mockito.mock(CustomService.class);
#Test(expected=Exception.class)
public void updateCustomFieldThrowsException() throws CustomException {
//exception.expect(Exception.class);
CustomFieldRequest request = getRequest();
when(customService.updateCustomeField(request)).thenThrow(Exception.class);
//customController.updateCustomField(request.getId(), request);
}
}
As mentioned above if I change my expected exception to CustomException.class in the test like below:
#Test(expected=CustomException.class)
public void updateCustomFieldThrowsException() throws CustomException {
//exception.expect(Exception.class);
CustomFieldRequest request = getRequest();
when(customService.updateCustomeField(request)).thenThrow(CustomException.class);
//customController.updateCustomField(request.getId(), request);
}
I see the test failing with java.lang.AssertionError. I tried the #Rule annotation(shown commented above).
You need a default constructor for CustomException or create the exeception in your test like this: new CustomException("")
Related
Sample interceptor code
package com.my.interceptor;
public class MyInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle( ... ) throws Exception {
throw new Exception("test exception"); // throw test exception
}
}
Sample controller code
package com.my.controller;
#RestController
#RequiredArgsConstructor
public MyController {
#GetMapping( "/my" )
public ResponseEntity getMy() {
...
}
}
Sample exception handler code
package com.my.handler;
#RestControllerAdvice( basePackages = { "com.my.interceptor" } ) // Doesn't handle interceptor
// #RestControllerAdvice( basePackages = { "com.my.controller" } ) // Handle both interceptor and controller
public class MyExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler( value = { Exception.class } )
public void handleException( HttpServletRequest request, Exception e) {
...
}
}
I want to handle exception from interceptor without creating custom exceptions.
But basePackages = { "com.my.interceptor" } in MyExceptionHandler doesn't handle exception from interceptor.
When I change basePackages to basePackages = { "com.my.controller" }, it handles both interceptor and controller.
Why this happen and how can I handle interceptor's exception only?
I want to custom error message in AOP around annotations.
I used to use #RestControllerAdvice before but It didn't work in AOP around method.
it outputs default error message.
I tried to input message in try ~ catch I know it's weird like //// 1 or //// 2
But I can't get to the point :(
TransactionAspect class
#Around("execution(* com.bono.server.controller..*(..))")
#Transactional
public Object caculatePerformanceTime(ProceedingJoinPoint proceedingJoinPoint) {
Object result = null;
try {
result = proceedingJoinPoint.proceed();
} catch (CustomeException e) { ////// 1
throw new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
catch (Throwable throwable) { /////// 2
return new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
return result;
}
ErrorMessage class
#Getter
#Setter
public class ErrorMessage {
private int errorCode;
private String errorMessage;
public ErrorMessage(CustomError customError) {
this.errorCode = customError.errorCode();
this.errorMessage = customError.errorMessage();
}
}
GroupExceptionAdvice class
#RestControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class GroupExceptionAdvice extends ResponseEntityExceptionHandler {
//// 1
#ExceptionHandler(value = CustomeException.class)
public ResponseEntity<CustomErrorResponse> customhandleNotSatisfied(Exception ex, WebRequest request) {
CustomErrorResponse error = new CustomErrorResponse();
error.setTimestamp(LocalDateTime.now());
error.setError(ex.getMessage());
error.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
//// 2
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ExceptionHandler(value = CustomException.class)
public ErrorMessage handlerUnResolvedAddressException(MisTypingException e) {
return new ErrorMessage(CustomError.HTTP_400_MISTYPE);
}
}
{
"timestamp": "2019-08-12T01:14:16.467+0000",
"status": 500,
"error": "Internal Server Error",
"message": "class com.bono.server.config.exception.ErrorMessage cannot be cast to class org.springframework.http.ResponseEntity (com.bono.server.config.exception.ErrorMessage and org.springframework.http.ResponseEntity are in unnamed module of loader 'app')",
"path": "/bono/api/alarm"
}
I want to show like this
{
"code" : 102,
"message" : "got it !"
}
Converting my previous comments into an answer because the OP said that they solved his problem.
Your ErrorMessage class does not extend any Exception or Throwable, so how can you throw it? The code should not even compile and yield a compile error like:
No exception of type ErrorMessage can be thrown;
an exception type must be a subclass of Throwable
I.e. in your sample class I you ought to write something like
public class ErrorMessage extends Exception {
// (...)
}
for a checked exception or
public class ErrorMessage extends RuntimeException {
// (...)
}
for a non-checked exception. But your class definition does not extend anything, i.e. implicitly it directly extends Object.
I have a test case where am throwing exception incase of some basic validation. but ExceptionMapper is not being invoked. But if i run from postman to hit the service it is working fine.
Do Junit test have to run differently for ExceptionMapper ?
Test case :
#Test
public void itShouldHavePersonNumber() {
RestAuthController controller = new RestAuthController();
Response response = controller.insertGuid(null, "m012");
assertThatExceptionOfType(ValidationException.class).isThrownBy(() -> {controller.insertGuid(null, "m012");});
assertThat(response.getStatus()).isEqualTo(Status.BAD_REQUEST.getStatusCode());
}
Controller:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response insertGuid(#QueryParam("personNumber") Integer personNumber, #QueryParam("guId") String guId ) throws ValidationException {
if(guId == null || guId.isEmpty()) {
throw new ValidationException("guId is Required");
}
}
Exception Mapper :
#Provider
public class ValidationMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException ex) {
return Response.status(Response.Status.BAD_REQUEST).entity(ex.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}
Exception:
public class ValidationException extends Exception {
/**
*
*/
private static final long serialVersionUID = 1L;
public ValidationException() {
super();
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(String message) {
super(message);
}
}
Why do you think the exception mapper should be called? It is not an integration test. All you are doing is instantiating the class and then calling a method. There is nothing magical in Java that will make the exception mapper be called. You need to run an integration test with the Jersey application running (and the mapper registered) if you want the mapper to be called.
One way to run an integration test with Jersey is to use it's Test Framework. Below is an example.
public class ValidationExceptionTest extends JerseyTest {
public static class ValidationException extends RuntimeException {}
public static class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
#Override
public Response toResponse(ValidationException e) {
return Response.status(400).entity("boo boo").build();
}
}
#Path("echo-name")
public static class EchoNameResource {
#GET
public String echoName(#QueryParam("name") String name) {
if (name == null || name.isEmpty()) {
throw new ValidationException();
}
return name;
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(EchoNameResource.class)
.register(ValidationExceptionMapper.class);
}
#Test
public void testResponseOkWithQueryParam() {
final Response response = target("echo-name")
.queryParam("name", "peeskillet")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(String.class)).isEqualTo("peeskillet");
}
#Test
public void testResponseBadRequestWithNoQueryParam() {
final Response response = target("echo-name")
.request()
.get();
assertThat(response.getStatus()).isEqualTo(400);
}
}
In my main controller, when the exception is thrown, I want it to be catched by the ExceptionHandler in my error handling controller, but that never happens. Instead, I am getting Error 500. I am suspecting the problem is in #ResponseBody annotation of my main controller. Any idea how to achieve wanted behavior?
Main controller
#RequestMapping(value = "/person/{person}", method = RequestMethod.GET)
public #ResponseBody Person execute(#PathVariable(value = "person") String person) {
if(person.isValid(person)) {
return person;
} else {
throw new ResourceNotFoundException("Invalid person format.");
}
}
Exception
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message, Throwable throwable) {
super(message, throwable);
}
public ResourceNotFoundException(Throwable throwable) {
super(throwable);
}
}
Error controller
private static final String ERROR_PAGE = "errors/error.jsp";
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ExceptionHandler(ResourceNotFoundException.class)
public ModelAndView invalidApiCall(){
return generateView(ERROR_404);
}
private ModelAndView generateView(String errorCode) {
return new ModelAndView(ERROR_PAGE);
}
My error view never gets generated (#ExceptionHandler never catches the exception). Instead I am getting error 500. Is there a way for ExceptionHandler to catch my exception?
Try to add #ControllerAdvice annotation for the Error Controller. If it is already added, check whether the class' package is included in package scan.
I am trying to test the following class:
package com.myclass;
public class MyClass {
private Map<String, String> dataMap = new HashMap<>();
public void extractInfoFromLine(String line) throws InvalidInputException {
String[] words = line.split(" ");
if (dataMap.containsKey(words[0])) {
throw new InvalidInputException();
}
dataMap.put(words[0], words[2]);
}
public void confirmInfoPresent(String name) {
// Do something
}
}
Using this TestNG class:
package com.myclass;
public class MyClassTest {
private MyClass myClass;
#DataProvider(name = "invalid-data-provider")
public Object[][] invalidDataProvider() {
return new Object[][] {
{ "A is B", "A"},
{ "A is D", "A"},
};
}
#BeforeTest()
public void setup() {
myClass = new MyClass();
}
#Test(dataProvider = "invalid-data-provider", expectedExceptions = InvalidInputException.class)
public void testExceptionalExtractValueForKey(String line, String name) throws InvalidInputException {
myClass.extractInfoFromLine(line);
myClass.confirmInfoPresent(name);
}
}
I have defined the following custom exception for this:
package com.myclass;
public class InvalidInputException extends Exception {
public InvalidInputException() {
super();
}
public InvalidInputException(String message) {
super(message);
}
public InvalidInputException(String message, Throwable cause) {
super(message, cause);
}
public InvalidInputException(Throwable cause) {
super(cause);
}
}
However, when I run this test case, I get the following error:
Expected exception com.myclass.InvalidInputException but got org.testng.TestException:
Method MyClassTest.testExceptionalExtractValueForKey(java.lang.String, java.lang.String)[pri:0, instance:com.myclass.MyClassTest#3930015a] should have thrown an exception of class com.myclass.InvalidInputException
I tried replacing my custom exception with a standard exception and still got the same result.
Can someone please help me with this?
#KarthickS, three different people gave you same answer already. I don't understand what else should be explained and how.
On the first #Test iteration dataMap is empty (dataMap.containsKey(words[0]) == false), so, the code inside "if" statement body will not be executed; which means that your exception will not be thrown.
TestNg will threat the test as failed since there is "expectedExceptions" attribute set in your #Test annotation. The second test iteration will pass since dataMap is not empty anymore (dataMap.put(words[0], words[2]) - you've added data on the first run).
Once again, TestNg doc says about "expectedExceptions": The list of exceptions that a test method is expected to throw. If no exception or a different than one on this list is thrown, this test will be marked a failure.