Java Test for Spring ExceptionalHandler - java

I have a springboot project with controllers and servies. And a GlobalExceptionHandler like -
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<Object> handle(DataIntegrityViolationException e, WebRequest request) {
....
String requestPath = ((ServletWebRequest)request).getRequest().getRequestURI();
// I am using this requestPath in my output from springboot
...
}
}
Can someone please tell me how to write mock this in my unit test class
((ServletWebRequest)request).getRequest().getRequestURI()

Unfortunately there is no support for subbing final methods in Mockito. You can use a other mocking framework like PowerMock.
I prefer in this cases to eliminate the need of mocking with an protected method:
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<Object> handle(final DataIntegrityViolationException e, final WebRequest request) {
final String requestPath = getRequestUri(request);
return ResponseEntity.ok().body(requestPath);
}
protected String getRequestUri(final WebRequest request) {
return ((ServletWebRequest) request).getRequest().getRequestURI();
}
}
And anonymous class in test:
public class GlobalExceptionHandlerTests {
private final GlobalExceptionHandler handler = new GlobalExceptionHandler() {
#Override
protected String getRequestUri(final org.springframework.web.context.request.WebRequest request) {
return "http://localhost.me";
};
};
#Test
void test() throws Exception {
final ResponseEntity<Object> handled = handler.handle(new DataIntegrityViolationException(""),
null);
assertEquals("http://localhost.me", handled.getBody());
}
}

Related

ExceptionHandler not working my resume project

#Order(Ordered.HIGHEST_PRECEDENCE)
#RestControllerAdvice(basePackages = "com.cvresumeproject.cvresumeproject.service.*")
public class RestExceptionHandler {
#ExceptionHandler(value = {CustomNotFoundException.class})
protected ResponseEntity<Object> handleEntityNotFound(CustomNotFoundException exception){
ApiError apiError =new ApiError(HttpStatus.NOT_FOUND);
apiError.setMessage(exception.getMessage());
return new ResponseEntity<>(apiError,apiError.getStatus());
}
}
Main class
#SpringBootApplication
#ComponentScan("com.cvresumeproject.cvresumeproject.ExceptionHandler.RestExceptionHandler")
public class CvresumeprojectApplication {
public static void main(String[] args) {
SpringApplication.run(CvresumeprojectApplication.class, args);
}
}
Using my customnotfoundexception
#Override
public TemplateDto findById(Long id) throws CustomNotFoundException {
return templateMapper.templateToTemplateDto(templateRepository.findById(id).get());
}
My resume project using exceptionhandler but this exception handler not working please help me Thanks!!!
You don't need * in the basePackages element in #RestControllerAdvice. Controllers that belong to the configured base packages or sub-packages thereof will be included, so you don't need the wildcard. Use the following instead:
#RestControllerAdvice(basePackages = "com.cvresumeproject.cvresumeproject.service")
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {CustomNotFoundException.class})
protected ResponseEntity<Object> handleEntityNotFound(Exception exception, WebRequest webRequest) {
HttpStatus errorCode = HttpStatus.NOT_FOUND;
ApiError apiError = new ApiError(errorCode);
apiError.setMessage(exception.getMessage());
return this.handleExceptionInternal(exception, apiError, new HttpHeaders(), errorCode, webRequest);
}
}

How to replace ErrorController deprecated function on Spring Boot?

Have a custom error controller on Spring boot:
package com.example.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.boot.web.servlet.error.ErrorController;
import javax.servlet.http.HttpServletRequest;
#Controller
public class CustomErrorController implements ErrorController
{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
#Override
public String getErrorPath()
{
return "/error";
}
}
But, when compile says: getErrorPath() in ErrorController has been deprecated. Ok, i found information: use server.error.path property. Ok, add this in application.properties and delete the function, but now says: CustomErrorController is not abstract and does not override abstract method getErrorPath() in ErrorController, ¿need a deprecated function?.
How to made the custom error controller?, the ErrorController requires getErrorPath but it is deprecated, what is the correct alternative?.
Starting version 2.3.x, Spring boot has deprecated this method. Just return null as it is anyway going to be ignored. Do not use #Override annotation if you want to prevent future compilation error when the method is totally removed. You can also suppress the deprecation warning if you want, however, the warning (also the #Override annotation) is helpful to remind you to cleanup/fix your code when the method is removed.
#Controller
#RequestMapping("/error")
#SuppressWarnings("deprecation")
public class CustomErrorController implements ErrorController {
public String error() {
// handle error
// ..
}
public String getErrorPath() {
return null;
}
}
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping("/error")
public ModelAndView handleError(HttpServletResponse response) {
int status = response.getStatus();
if ( status == HttpStatus.NOT_FOUND.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-404");
} else if (status == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-500");
}
System.out.println(status);
return new ModelAndView("error");
}
}
there is an #ControllerAdvice annotation
#ControllerAdvice
public class MyErrorController {
#ExceptionHandler(RuntimeException.class)
public String|ResponseEntity|AnyOtherType handler(final RuntimeException e) {
.. do handle ..
}
#ExceptionHandler({ Exception1.class, Exception2.class })
public String multipleHandler(final Exception e) {
}
}
To handle errors, There is no need to define a controller class
implementing an error controller.
To handle errors in your entire application instead of writing
#Controller
public class CustomErrorController implements ErrorController{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
}
use the below class
#ControllerAdvice
public class myExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClassexceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// Its an example you can define a class with your own structure
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(CustomException.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage()); // For reference
return new ResponseEntity<YourResponseClass>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(BadCredentialsException.class)
public final ResponseEntity<YourResponseClass> handleBadCredentialsException(BadCredentialsException ex, WebRequest request){
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// For refernece
return new ResponseEntity<>(exceptionResponse, HttpStatus.UNAUTHORIZED);
}
}
The class above annoted with #ControllerAdvice acts as custom exception handler and it handles all the expecptions thrown by ur application. In above code sample only three exceptions are showed for understanding. It can handle many exceptions
In your application if there's any exception thrown it will come to this class and send the response back. You can have a customized message and structure as per ur needs.
#Controller
public class AppErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if(status != null) {
int statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.FORBIDDEN.value()) {
return "errorpages/error-403";
} else if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "errorpages/error-404";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "errorpages/error-500";
}
}
return "errorpages/error";
}
}

MockMvc and #ControllerAdvice doesn't work in tests

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?
You can try importing your exception handler in your test class:
#RunWith(SpringRunner.class)
#Import(RestExceptionHandler.class) // EXCEPTION HANDLER CLASS
public class SearchControllerTest {

Why is EJB Injection not working on Interceptor?

I created an interceptor that intercepts the call to my method and also its response. What happens is that I have to use a service while intercepting and the service works fine when intercepted on the request, but is null when intercepted on the response.
I tried separating the interceptor so the implementation of WriterInterceptor was in another class, but it seems to behavior the same.
#POST
#Path("/test")
#MyAnnotation
public void test(
final #Suspended AsyncResponse response,
final #ApiParam(value = "Test.", required = true) #FormParam("test") String test)
throws ServiceException {
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new AsyncResponseCallable(response) {
#Override
public Response execute() throws Exception {
Object object = service.foo();
Status status = Status.OK;
return Response.status(status).entity(object).build();
}
});
}
#Provider
#MyAnnotation
public class MyInterceptor implements WriterInterceptor {
#EJB
private MyService myService;
#AroundInvoke
public Object interceptor(InvocationContext context) throws Exception {
myService.test();
return context.proceed();
}
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws WebApplicationException {
myService.test();
context.proceed();
}
}
#Stateless
public class MyService {
public void test() {
System.out.println("Done");
}
}
The method aroundWriteTo should have an instance of myService injected by the EJB annotation, but it is null.
Can you guys help me figure this out?

Testing Spring #MVC annotations

I ran into a problem the other day where a #Valid annotation was accidentally removed from a controller class. Unfortunately, it didn't break any of our tests. None of our unit tests actually exercise the Spring AnnotationMethodHandlerAdapter pathway. We just test our controller classes directly.
How can I write a unit or integration test that will correctly fail if my #MVC annotations are wrong? Is there a way I can ask Spring to find and exercise the relevant controller with a MockHttpServlet or something?
I write integration tests for this kind of thing. Say you have a bean with validation annotations:
public class MyForm {
#NotNull
private Long myNumber;
...
}
and a controller that handles the submission
#Controller
#RequestMapping("/simple-form")
public class MyController {
private final static String FORM_VIEW = null;
#RequestMapping(method = RequestMethod.POST)
public String processFormSubmission(#Valid MyForm myForm,
BindingResult result) {
if (result.hasErrors()) {
return FORM_VIEW;
}
// process the form
return "success-view";
}
}
and you want to test that the #Valid and #NotNull annotations are wired correctly:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:web/WEB-INF/application-context.xml",
"file:web/WEB-INF/dispatcher-servlet.xml"})
public class MyControllerIntegrationTest {
#Inject
private ApplicationContext applicationContext;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;
#Before
public void setUp() throws Exception {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
}
ModelAndView handle(HttpServletRequest request, HttpServletResponse response)
throws Exception {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
// if you want to override any injected attributes do it here
final HandlerInterceptor[] interceptors =
handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
final ModelAndView mav = handlerAdapter.handle(request, response, controller);
return mav;
}
#Test
public void testProcessFormSubmission() throws Exception {
request.setMethod("POST");
request.setRequestURI("/simple-form");
request.setParameter("myNumber", "");
final ModelAndView mav = handle(request, response);
// test we're returned back to the form
assertViewName(mav, "simple-form");
// make assertions on the errors
final BindingResult errors = assertAndReturnModelAttributeOfType(mav,
"org.springframework.validation.BindingResult.myForm",
BindingResult.class);
assertEquals(1, errors.getErrorCount());
assertEquals("", errors.getFieldValue("myNumber"));
}
See my blog post on integration testing Spring's MVC annotations
Sure. There's no reason why your test can't instantiate its own DispatcherServlet, inject it with the various items which it would have in a container (e.g. ServletContext), including the location of the context definition file.
Spring comes with a variety of servlet-related MockXYZ classes for this purpose, including MockServletContext, MockHttpServletRequest and MockHttpServletResponse. They're not really "mock" objects in the usual sense, they're more like dumb stubs, but they do the job.
The servlet's test context would have the usual MVC-related beans, plus your beans to test. Once the servlet is initialized, create the mock requests and responses, and feed them into the servet's service() method. If request gets routed correctly, you can check the results as written to the mock response.
In upcoming spring 3.2 (SNAPSHOT available) or with spring-test-mvc (https://github.com/SpringSource/spring-test-mvc) you can do it like this:
first we emulate Validation as we do not want to test the validator, just want to know if validation is called.
public class LocalValidatorFactoryBeanMock extends LocalValidatorFactoryBean
{
private boolean fakeErrors;
public void fakeErrors ( )
{
this.fakeErrors = true;
}
#Override
public boolean supports ( Class<?> clazz )
{
return true;
}
#Override
public void validate ( Object target, Errors errors, Object... validationHints )
{
if (fakeErrors)
{
errors.reject("error");
}
}
}
this is our test class:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration
public class RegisterControllerTest
{
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
#InjectMocks
private RegisterController registerController;
#Autowired
private LocalValidatorFactoryBeanMock validator;
#Before
public void setup ( )
{
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
// if you want to inject mocks into your controller
MockitoAnnotations.initMocks(this);
}
#Test
public void testPostValidationError ( ) throws Exception
{
validator.fakeErrors();
MockHttpServletRequestBuilder post = post("/info/register");
post.param("name", "Bob");
ResultActions result = getMockMvc().perform(post);
// no redirect as we have errors
result.andExpect(view().name("info/register"));
}
#Configuration
#Import(DispatcherServletConfig.class)
static class Config extends WebMvcConfigurerAdapter
{
#Override
public Validator getValidator ( )
{
return new LocalValidatorFactoryBeanMock();
}
#Bean
RegisterController registerController ( )
{
return new RegisterController();
}
}
}

Categories

Resources