mercredi 16 novembre 2016

Testing REST endpoints with custom exception handling

I am working on a project with Spring microservices (modules) and I want to test my REST endpoint using MockMvc. My testing works fine for cases where the request is valid but it is not working when requesting a url that is invalid. By not working I mean my custom exception handler (@ControllerAdvice) does not get called, the exception gets thrown and the test fails.

My exception handler and testing class are implemented in different modules.

common-module (ExceptionHandler)

@ControllerAdvice
public class CoreExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<ErrorMessageDTO> handleException(Exception ex, HttpServletRequest request) {

        // Getting servlet request URL
        String uri = request.getRequestURI();
        HttpStatus a;

        ErrorMessageDTO errorMessage;

        if (ex instanceof CoreException) {
            CoreException e = (CoreException) ex;
            ...
            errorMessage = new ErrorMessageDTO(e, uri);
        } else {
            errorMessage = new ErrorMessageDTO(ex, uri);
            ...
        }
        return new ResponseEntity<ErrorMessageDTO>(errorMessage, a);
    }
}

country-module

This is where my REST endpoint and Testing class are implemented. The common module dependency is included in this module's pom.xml and the packages are scanned through the main class.

CountryApplication.java

@EnableCaching
@EnableDiscoveryClient
@EnableAspectJAutoProxy
@SpringBootApplication(scanBasePackages = {
    "com.something1.something2.something3.common.exception",
    "com.something1.something2.something3.common.util.logged",
    "com.something1.something2.something3.country"
})
public class CountryApplication {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(CountryApplication.class, args);
    }
    ...
}

CountryService.java

This is a method in my Service class.

@GetMapping("/{id:\\d+}")
public CountryDTO getCountryById(@PathVariable("id") Integer id) throws CoreException {

    Country countryEntity = this.countryRepository.findOne(id);

    // requesting for id that does not exist
    if (countryEntity == null) {
        throw new CoreException(CoreError.ENTITY_NOT_FOUND);
    }
    return this.countryMapper.daoToDto(countryEntity);
}

CountryServiceTest.java

@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureTestDatabase
@RunWith(SpringRunner.class)
public class CountryServiceTest {
    ...
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void getByIdTest() throws Exception {

        // Get by id exists
        mockMvc.perform(get("/2"))
            .andExpect(status().isOk())
            .andExpect(content().contentType(contentType))
            .andDo(print());

        // Get by id not exists. NOT WORKING
          mockMvc.perform(get("/100000"))
            .andExpect(status().isNotFound())
            .andExpect(content().contentType(contentType));

    }
}

As I described above, the problem is that at the second request of the test method, the CoreExceptionHandler does not get called and the test fails throwing a:

NestedServletException: Request processing failed; nested exception is com.something1.something2.something3.common.exception.CoreException.

The dependency for the common module is well configured (at least when I am deploying in non-test mode) since I am using it for other things too, plus the ExceptionHandler gets called when I am not testing.

Another strange thing is that when I am deploying my Test, Spring Boot's logs show that the CoreExceptionHandler gets detected. This is the line. Detected @ExceptionHandler methods in coreExceptionHandler

Aucun commentaire:

Enregistrer un commentaire