Write JUnit test for @ExceptionHandler

24,950

Solution 1

Consider using Spring 3.2 and its mvc-test-framework

import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml")
public class WebMvcTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void getFoo() throws Exception {
        this.mockMvc.perform(
            get("/testx")
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            )
            .andExpect(status().isUnauthorized());
    }
}

Controller code

@Controller
public class MyController {

    public class MyException extends RuntimeException {
    };

    @RequestMapping("/testx")
    public void myMethod() {
        throw new MyException();

    }

    @ExceptionHandler(MyException.class)
    @ResponseStatus(value = HttpStatus.UNAUTHORIZED, reason = "blah")
    public void handler() {
        System.out.println("handler processed");
    }
}

This "test" passes well.

Disclaimer: currently I'm a noob in Spring MVC testing, actually it's my first test.
upd: Thanks to The Drake for the correction.

Solution 2

Annotate your Exception Handling controller with @ControllerAdvice instead of @Controller.

As Boris Treukhov noted when adding the @ExceptionHandler annotation to a method in the controller that throws the exception will make it work but only from that specific controller.

@ControllerAdvice will allow your exception handeling methods to be applicable for your whole application not just one specific controller.

Share:
24,950
John B
Author by

John B

Software Engineer Baltimore, MD

Updated on July 09, 2022

Comments

  • John B
    John B almost 2 years

    I am writing a Rest service using Spring MVC. Here is the outline of the class:

     @Controller
     public class MyController{
    
         @RequestMapping(..)
         public void myMethod(...) throws NotAuthorizedException{...}
    
         @ExceptionHandler(NotAuthorizedException.class)
         @ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="blah")
         public void handler(...){...}
     }
    

    I have written my unit tests using the design posted here. The test is basically as follows:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(....)
    public class mytest{
    
        MockHttpServletRequest requestMock;
        MockHttpServletResponse responseMock;
        AnnotationMethodHandlerAdapter handlerAdapter;
    
    @Before
    public void setUp() {
        requestMock = new MockHttpServletRequest();
        requestMock.setContentType(MediaType.APPLICATION_JSON_VALUE);
        requestMock.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
    
        responseMock = new MockHttpServletResponse();
    
        handlerAdapter = new AnnotationMethodHandlerAdapter();
    }
    
    @Test
    public void testExceptionHandler(){
        // setup ....
        handlerAdapter.handle(...);
    
        // verify
        // I would like to do the following
        assertThat(responseMock.getStatus(), is(HttpStatus.UNAUTHORIZED.value()));
    }
    
    }
    

    However, the call to handle is throwing the NotAuthorizedException. I have read that this is by design to be able to unit test that the method throws the appropriate exception, however I would like to write an automated test that the framework is handling this exception appropriately and that the class under test has implemented the handler appropriately. Is there a way to do this?

    Please be aware that I do not have access to the actual code in a place where I could post it.

    Also, I am limited (for unfortunate reasons) to Spring 3.0.5 or 3.1.2.

  • John B
    John B over 11 years
    I am already using a ExpectedException rule to do this. This just allows the test to pass when the exception is thrown, it does not test that the framework is properly passing the exception to the @ExceptionHandler method.
  • Joe
    Joe over 11 years
    If you're using an ExpectedException rule it is not in your posted post anywhere and would have been good to list there. Like @flup said then, test your own code, not the framework.
  • John B
    John B over 11 years
    This looks like the correct solution. Thank you. Unfortunately I am limited to Spring 3.0.5. If no-one comes up with a solution I can use with that version I will mark this as the correct answer.
  • Foolish
    Foolish about 7 years
    This is not working for me I am using MockMvcBuilders.standaloneSetup()