Write JUnit test for @ExceptionHandler
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.
Comments
-
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 theNotAuthorizedException
. 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 over 11 yearsI 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 over 11 yearsIf 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 over 11 yearsThis 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 about 7 yearsThis is not working for me I am using
MockMvcBuilders.standaloneSetup()