Comment tester un contrôleur Spring MVC avec @PathVariable?

J’ai un simple contrôleur annoté similaire à celui-ci:

@Controller public class MyController { @RequestMapping("/{id}.html") public Ssortingng doSomething(@PathVariable Ssortingng id, Model model) { // do something return "view"; } } 

et je veux le tester avec un test unitaire comme celui-ci:

 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 } } 

Le problème est que la méthode AnnotationMethodHandlerAdapter.handler () renvoie une 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) 

    Depuis le spring 3.2, il existe un moyen approprié de le tester de manière élégante et simple. Vous pourrez faire des choses comme ceci:

     @RunWith(SpringJUnit4ClassRunner.class) @WebAppConfiguration @ContextConfiguration("servlet-context.xml") public class SampleTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setup() { this.mockMvc = webAppContextSetup(this.wac).build(); } @Test public void getFoo() throws Exception { this.mockMvc.perform(get("/foo").accept("application/json")) .andExpect(status().isOk()) .andExpect(content().mimeType("application/json")) .andExpect(jsonPath("$.name").value("Lee")); } } 

    Pour plus d’informations, consultez http://blog.springsource.org/2012/11/12/spring-framework-3-2-rc1-spring-mvc-test-framework/

    J’appellerais ce que vous recherchez après un test d’intégration basé sur la terminologie du manuel de référence de Spring. Que diriez-vous de faire quelque chose comme:

     import static org.springframework.test.web.ModelAndViewAssert.*; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({/* include live config here eg "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 } } 

    Pour plus d’informations, j’ai écrit une entrée de blog sur l’intégration des annotations Spring MVC .

    Un cadre prometteur pour tester Spring MVC https://github.com/SpringSource/spring-test-mvc

    Le message d’exception fait référence à une variable “feed”, qui n’est pas présente dans votre exemple de code. Elle est probablement due à quelque chose que vous ne nous avez pas montré.

    En outre, votre test teste Spring et votre propre code. Est-ce vraiment ce que vous voulez faire?

    Il est préférable de supposer que Spring fonctionne (ce qu’il fait) et qu’il suffit de tester votre propre classe, c’est-à-dire d’appeler directement MyController.doSomething() . C’est l’un des avantages de l’approche par annotation – vous n’avez pas besoin d’utiliser des requêtes et des réponses fictives, vous utilisez simplement des POJO de domaine.

    Si vous utilisez Spring 3.0.x.

    Ici, je suggère une fusion des réponses Emil et scarba05 en utilisant le test de spring et non le test de spring-mvc. Veuillez ignorer cette réponse et vous reporter aux exemples spring-test-mvc si vous utilisez Spring 3.2.x ou une version ultérieure

    MyControllerWithParameter.java

     @Controller public class MyControllerWithParameter { @RequestMapping("/testUrl/{pathVar}/some.html") public Ssortingng passOnePathVar(@PathVariable Ssortingng pathVar, ModelMap model){ model.addAtsortingbute("SomeModelAtsortingbute",pathVar); return "viewName"; } } 

    MyControllerTest.java

     import static org.springframework.test.web.ModelAndViewAssert.assertViewName; import java.util.HashMap; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.ModelAndViewAssert; import org.springframework.web.servlet.HandlerAdapter; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"file:src\\main\\webapp\\WEB-INF\\spring\\services\\servlet-context.xml" }) public class MyControllerTest { private MockHttpServletRequest request; private MockHttpServletResponse response; private HandlerAdapter handlerAdapter; @Before public void setUp() throws Exception { request = new MockHttpServletRequest(); response = new MockHttpServletResponse(); this.handlerAdapter = applicationContext.getBean(AnnotationMethodHandlerAdapter.class); } // Container beans private MyControllerWithParameter myController; private ApplicationContext applicationContext; public ApplicationContext getApplicationContext() { return applicationContext; } @Autowired public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } public MyControllerWithParameter getMyController() { return myController; } @Autowired public void setMyController(MyControllerWithParameter myController) { this.myController = myController; } @Test public void test() throws Exception { request.setRequestURI("/testUrl/Irrelavant_Value/some.html"); HashMap pathvars = new HashMap(); // Populate the pathVariable-value pair in a local map pathvars.put("pathVar", "Path_Var_Value"); // Assign the local map to the request atsortingbute concerned with the handler mapping request.setAtsortingbute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathvars); final ModelAndView modelAndView = this.handlerAdapter.handle(request, response, myController); ModelAndViewAssert.assertAndReturnModelAtsortingbuteOfType(modelAndView, "SomeModelAtsortingbute", Ssortingng.class); ModelAndViewAssert.assertModelAtsortingbuteValue(modelAndView, "SomeModelAtsortingbute", "Path_Var_Value"); ModelAndViewAssert.assertViewName(modelAndView, "viewName"); } 

    }

    J’ai constaté que vous pouvez insérer manuellement un mappage PathVariable dans l’object de requête. Ceci est clairement non idéal mais semble fonctionner. Dans votre exemple, quelque chose comme:

     @Test public void test() { MockHttpServletRequest request = new MockHttpServletRequest(); request.setRequestURI("/test.html"); HashMap pathvars = new HashMap(); pathvars.put("id", "test"); request.setAtsortingbute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathvars); new AnnotationMethodHandlerAdapter().handle(request, new MockHttpServletResponse(), new MyController()); // assert something } 

    Je serais certainement intéressé à trouver une meilleure option.

    Je ne suis pas sûr que ma réponse initiale va aider avec @PathVariable. Je viens d’essayer de tester un @PathVariable et j’obtiens l’exception suivante:

    org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Impossible d’invoquer la méthode du gestionnaire [public org.springframework.web.servlet.ModelAndView test.MyClass.myMethod (test.SomeType)]; l’exception nestede est java.lang.IllegalStateException: impossible de trouver @PathVariable [nomParamètre] dans @RequestMapping

    La raison en est que les variables de chemin dans la requête sont analysées par un intercepteur. L’approche suivante fonctionne pour moi:

     import static org.springframework.test.web.ModelAndViewAssert.*; @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 atsortingbutes 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 testDoSomething() throws Exception { request.setRequestURI("/test.html"); request.setMethod("GET"); final ModelAndView mav = handle(request, response); assertViewName(mav, "view"); // assert something else } 

    J’ai ajouté un nouvel article de blog sur les tests d’intégration des annotations mvc du spring