I have a simple annotated controller similar to this one:
@Controller public class MyController { @RequestMapping("/{id}.html") public String doSomething(@PathVariable String id, Model model) { // do something return "view"; } }
and I want to test it with an unit test like this:
public class MyControllerTest { @Test public void test() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("/test.html"); new AnnotationMethodHandlerAdapter() .handle(request, new MockHttpServletResponse(), new MyController()); // assert something } }
The problem is that AnnotationMethodHandlerAdapter.handler() method throws an exception:
java.lang.IllegalStateException: Could not find @PathVariable [id] in @RequestMapping at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.resolvePathVariable(AnnotationMethodHandlerAdapter.java:642) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolvePathVariable(HandlerMethodInvoker.java:514) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:262) at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:146)
The @PathVariable annotation is used to extract the value from the URI. It is most suitable for the RESTful web service where the URL contains some value. Spring MVC allows us to use multiple @PathVariable annotations in the same method. A path variable is a critical part of creating rest resources.
No. A unit test is to test a single component in isolation. Using constructor injection in your beans allows you to very simply call new SomeService(myMock) , no Spring required.
I'd call what you're after an integration test based on the terminology in the Spring reference manual. How about doing something like:
import static org.springframework.test.web.ModelAndViewAssert.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({/* include live config here e.g. "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; private MyController controller; @Before public void setUp() { request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); handlerAdapter = applicationContext.getBean(HandlerAdapter.class); // I could get the controller from the context here controller = new MyController(); } @Test public void testDoSomething() throws Exception { request.setRequestURI("/test.html"); final ModelAndView mav = handlerAdapter.handle(request, response, controller); assertViewName(mav, "view"); // assert something } }
For more information I've written a blog entry about integration testing Spring MVC annotations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With