I'm testing my RestController
with mockMvc
. I have a global RestExceptionHandler
to resolve all exceptions. In my RestController
I throw custom Exception
RequestValidationException
like this:
@ApiOperation("Search something")
@RequestMapping(path = "/search", method = RequestMethod.POST)
public CompletableFuture<SomeResponse> search(
@RequestBody @Validated SearchRequest request, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
throw new RequestValidationException(bindingResult);
}
return searchService.search(request);
}
And when i pass empty request it must throw RequestValidationException(bindingResult)
but when i start tests they fall in that place where i throw Exception
instead to resolve it.
i try to configure my mockMvc
like this:
@RunWith(SpringRunner.class)
public class SearchControllerTest {
private MockMvc mockMvc;
@InjectMocks
protected SearchController searchController;
@MockBean
private SearchService searchService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(searchController)
.setHandlerExceptionResolvers(getHandlerExceptionResolver())
.build();
}
private HandlerExceptionResolver getHandlerExceptionResolver() {
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("exceptionHandler", RestExceptionHandler.class);
final WebMvcConfigurationSupport webMvcConfigurationSupport = new WebMvcConfigurationSupport();
webMvcConfigurationSupport.setApplicationContext(applicationContext);
return webMvcConfigurationSupport.handlerExceptionResolver();
}
but it doesnt help. i'm getting an Exception
insted json with message.
My RequestValidationExceptionHandler
:
@Component
public class RequestValidationExceptionHandler implements ApiExceptionHandler {
@Override
public ResponseEntity<ApiResponse> process(Throwable throwable) {
RequestValidationException e = (RequestValidationException) throwable;
if (e.getBindingResult() != null) {
return new ResponseEntity<>(ApiResponse.badRequest(e.getBindingResult()), HttpStatus.OK);
}
return new ResponseEntity<>(ApiResponse.badRequest(throwable, ApiResponseCode.BAD_REQUEST), HttpStatus.OK);
}
@Override
public Class<? extends Throwable> getSupportedException() {
return RequestValidationException.class;
}
}
2) My @ControllerAdvice
:
@Slf4j
@ControllerAdvice
@SuppressWarnings({"checkstyle:JavadocMethod", "checkstyle:MultipleStringLiterals"})
public class RestExceptionHandler {
@Autowired
private ExceptionHandlerRegistry handlerRegistry;
@ExceptionHandler
public ResponseEntity handleThrowable(Throwable throwable, WebRequest request) {
request.setAttribute(Constants.ERROR_ATTRIBUTE_NAME, throwable, RequestAttributes.SCOPE_REQUEST);
Throwable ex = throwable instanceof CompletionException ?
ObjectUtils.defaultIfNull(throwable.getCause(), throwable) : throwable;
for (ApiExceptionHandler handler : handlerRegistry.getHandlers()) {
if (handler.isSupported(ex)) {
return handler.process(ex);
}
}
return new ResponseEntity<>(ApiResponse.badRequest(throwable, ApiResponseCode.SERVER_ERROR), HttpStatus.OK);
}
}
3) And ExceptionHandlerRegistry
:
@Component
public class ExceptionHandlerRegistry {
@Getter
private final List<ApiExceptionHandler> handlers;
@Autowired
public ExceptionHandlerRegistry(List<ApiExceptionHandler> handlers) {
this.handlers = ObjectUtils.defaultIfNull(handlers, Collections.emptyList());
}
}
The Error message:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is ru.filit.mvideo.mb2c.api.exceptions.RequestValidationException
UPDATE So after some discussion with @MichaelMichailidis, i try to do this, i just add an inner @Configuration class with needed beans:
@TestConfiguration
static class SearchControllerTestConfiguration {
@Bean
public RequestValidationExceptionHandler requestValidationExceptionHandler(){
return new RequestValidationExceptionHandler();
}
@Bean
public ExceptionHandlerRegistry getExceptionHandlerRegistry(final RequestValidationExceptionHandler requestValidationExceptionHandler){
return new ExceptionHandlerRegistry(Collections.singletonList(requestValidationExceptionHandler));
}
@Bean
public RestExceptionHandler getRestExceptionHandler(){
return new RestExceptionHandler();
}
}
and my test pass. But i can't understand why test were working without configuration before i add @ControllerAdvice?