Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Controller Unit Test throws NestedServletException

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);
}   
like image 493
hades Avatar asked Jan 25 '23 19:01

hades


2 Answers

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

like image 176
i.bondarenko Avatar answered Jan 28 '23 08:01

i.bondarenko


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

like image 20
Aleksandr Semyannikov Avatar answered Jan 28 '23 08:01

Aleksandr Semyannikov