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?
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);
}
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 see two options for you:
NoDataFoundException
extending ServletException
. I am not sure if it would fit you, because it will make your exception checked.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:
- runtime exceptions or errors
- ServletExceptions or subclasses thereof
- IOExceptions or subclasses thereof
PS: If you are interested in reasons of why Spring handles exceptions in such way, I asked a question
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With