12

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.

Community
  • 1
  • 1
John B
  • 30,460
  • 6
  • 67
  • 92
  • 6
    In general, I'd say test your own code, not the framework. – flup Jan 30 '13 at 13:49
  • 1
    I suggest that testing the presense of `@ExceptionHandler` and that I have configured the framework correctly via an automated test is valuable. – John B Jan 30 '13 at 14:04
  • 1
    Certainly, but that might be beyond the scope of a unit test. Recall that in addition to your jUnit test, you're going to need a properly set up spring configuration file. Plus, your tests may have to change once you upgrade Spring. Generally speaking, I've found that if I've configured the framework correctly once, I tend to either configure it correctly everywhere, or else there's some fail fast that happens immediately when I try to deploy my app. – Peter Bratton Jan 30 '13 at 15:10
  • You might also consider that the framework itself has an exhaustive unit test suite, which is available for your inspection at any time. – Peter Bratton Jan 30 '13 at 15:11
  • I would suggest that what I am trying to test is not the framework but that I have the annotations set up properly. This is what is being cone in the case of `@RequestMapping` by using the `AnnotationMethodHandlerAdapter`. I am looking for the same sort of testing of my `@ExceptionHandler` annotation, an automated test that the appropriate response code is returned with the appropriate message. This is not testing the framework but my code. I could do reflection to look for the annotations in the class and check they are correct but it seems like there should be a better way. – John B Jan 30 '13 at 16:24

3 Answers3

8

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.

Community
  • 1
  • 1
Boris Treukhov
  • 16,478
  • 9
  • 66
  • 86
  • 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. – John B Jan 31 '13 at 11:40
  • This is not working for me I am using `MockMvcBuilders.standaloneSetup()` – Foolish Apr 10 '17 at 16:10
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.

Jonas Geiregat
  • 4,359
  • 3
  • 36
  • 56
-2

You could change @Test to

@Test(expected=NotAuthorizedException.class)

This would return true if the internals throw up that exception and false otherwise.

This would also make the assertThat() unnecessary. You could write a second test that catches the NotAuthorizedException then you could inspect the responseMock under that condition then.

Joe
  • 1,003
  • 12
  • 19
  • 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. – John B Jan 30 '13 at 14:47
  • 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. – Joe Jan 30 '13 at 14:58