8

I have a Spring controller that throws an error when there is no data. I want to test the exception, which is custom NoDataFoundException , but it always throws org.springframework.web.util.NestedServletException

The unit test error message is: java.lang.Exception: Unexpected exception, expected<com.project.NoDataFoundException> but was<org.springframework.web.util.NestedServletException>

Controller Unit Test

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {MockConfiguration.class})
@WebAppConfiguration
public class ModelControllerTest{

    private MockMvc mockMvc;

    @Inject
    private ModelController controller;

    @Inject
    private ResponseBuilder responseBuilder;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test(expected = NoDataFoundException.class)
    public void findAllModels_invalidRequest_throwNoDataFound() throws Exception {
        when(responseBuilder.findAll(any())).thenReturn(null);
        mockMvc
        .perform(get("/models"))
            .andExpect(status().isInternalServerError());
    }

}

Controller

@GetMapping("/models")
public ResponseEntity<List<Model>> findAllModels() {
    //Some Logic 
    dataExistenceCheck(response);
    return new ResponseEntity<>(response, HttpStatus.OK);
}

private <T> void dataExistenceCheck(T target) {
    if (target == null || (target instanceof Collection && ((Collection<?>) target).isEmpty())) {
        throw new NoDataFoundException();
    }
}

NoDataFoundException Class

public class NoDataFoundException extends RuntimeException {

    private static final long serialVersionUID = 140836818081224458L;

    public NoDataFoundException() {
        super();
    }

    public NoDataFoundException(String message) {
        super(message);
    }

}

Finding

I debugged the codes and traced until the org.springframework.web.servlet.FrameworkServlet class. Yes, I expected the NoDataFoundException but there it throws NesteServletException.

How can I solve that? What did I do wrong?

enter image description here


Edited Question

I have @ControllerAdvice and handle NoDataFoundException, but it hits NestedServletException before reaching here.

@ResponseBody
@ResponseStatus(value = HttpStatus.NOT_FOUND)
@ExceptionHandler(NoDataFoundException.class)
public ResponseEntity<ErrorResponse> noDataFoundExceptionHandler(NoDataFoundException exception) {
    LOGGER.debug("No data found exception [{}].", exception);
    return new ResponseEntity<>(new ErrorResponse("not found"), HttpStatus.NOT_FOUND);
}   
Sanket Patel
  • 227
  • 1
  • 14
hades
  • 4,294
  • 9
  • 46
  • 71

2 Answers2

10

NestedServletException is a wrapper/adapter that adopt all exceptions to javax.servlet.ServletException. It is part of java servlet API.

You can solve it in following ways:

1) Catch NestedServletException and rethrow cause:

try {
    mockMvc
    .perform(get("/models"))
        .andExpect(status().isInternalServerError());
} catch (NestedServletException e) {
    throw e.getCause();
}

2) Use org.assertj.core.api.Assertions.assertThatThrownBy

@Test
public void findAllModels_invalidRequest_throwNoDataFound() throws Throwable {
    Assertions.assertThatThrownBy(() ->
            mockMvc.perform(get("/models")).andExpect(status().isInternalServerError()))
            .hasCause(new NoDataFoundException(null));
}

3) In your controller or global exception handler add @ExceptionHandler(NoDataFoundException.class) and translate it into valid Http code and response body.

4) Extends NoDataFoundException from ServletException

i.bondarenko
  • 3,442
  • 3
  • 11
  • 21
  • 1
    thanks, i actually had controller advice that handles `@ExceptionHandler(NoDataFoundException.class)`, but the error not even reaching there, see my edited question. – hades Sep 26 '19 at 07:35
  • Your exception handler works perfectly. I have committed test into github. You can clone it by : https://github.com/ivan333m/mock.git Have a look at https://github.com/ivan333m/mock/blob/master/demo/src/test/java/com/example/demo/rest/ModelControllerTest.java I think exception handler is the best solution. – i.bondarenko Sep 26 '19 at 07:57
  • As you can see in my test I assert http code `status().is4xxClientError()` and content `.andExpect(content().string("{\"error\":\"not found\"}"));`. It is the evidence that your exception handler works fine. :) – i.bondarenko Sep 26 '19 at 08:15
  • @hades did you find a solution to this? Indeed it seems that controller exception handler is not triggered when an exception is thrown. – mvd Sep 16 '21 at 02:24
  • I am facing same issue but interestingly this issue occurs on Wildfly 17. Rest of the application servers behave as expected and the custom exception handler is getting invoked as expected. Why is only Widlfly server behaves in this odd way? – ykjs121 Jun 21 '22 at 10:49
1

I see two options for you:

  1. Make your NoDataFoundException extending ServletException. I am not sure if it would fit you, because it will make your exception checked.
  2. Use another approach to check what exception was thrown, like there

Actually, it's better to always write such sort of tests according to the second option, because according to Servlet Specification:

A servlet or filter may throw the following exceptions during processing of a request:

  1. runtime exceptions or errors
  2. ServletExceptions or subclasses thereof
  3. IOExceptions or subclasses thereof

PS: If you are interested in reasons of why Spring handles exceptions in such way, I asked a question