sirve requestmapping que para mvc modelandview ejemplo java spring unit-testing spring-mvc spring-test

java - para - requestmapping spring boot



¿Cómo probar un controlador Spring MVC usando @PathVariable? (7)

Tengo un controlador anotado simple similar a este:

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

y quiero probarlo con una prueba unitaria como esta:

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

El problema es que el método AnnotationMethodHandlerAdapter.handler () arroja una excepción:

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)


Siempre que esté utilizando Spring 3.0.x.

Aquí sugiero una fusión de respuestas de Emil y scarba05 usando spring-test, no spring-test-mvc. Saltee esta respuesta y consulte los ejemplos de spring-test-mvc si está utilizando Spring 3.2.xo posterior.

MyControllerWithParameter.java

@Controller public class MyControllerWithParameter { @RequestMapping("/testUrl/{pathVar}/some.html") public String passOnePathVar(@PathVariable String pathVar, ModelMap model){ model.addAttribute("SomeModelAttribute",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<String, String> pathvars = new HashMap<String, String>(); // Populate the pathVariable-value pair in a local map pathvars.put("pathVar", "Path_Var_Value"); // Assign the local map to the request attribute concerned with the handler mapping request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathvars); final ModelAndView modelAndView = this.handlerAdapter.handle(request, response, myController); ModelAndViewAssert.assertAndReturnModelAttributeOfType(modelAndView, "SomeModelAttribute", String.class); ModelAndViewAssert.assertModelAttributeValue(modelAndView, "SomeModelAttribute", "Path_Var_Value"); ModelAndViewAssert.assertViewName(modelAndView, "viewName"); }

}


A partir de Spring 3.2, hay una forma adecuada de probar esto, de una manera elegante y fácil. Podrás hacer cosas como esta:

@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")); } }

Para obtener más información, consulte http://blog.springsource.org/2012/11/12/spring-framework-3-2-rc1-spring-mvc-test-framework/


Descubrí que puede insertar manualmente una asignación PathVariable en el objeto de solicitud. Esto es claramente no ideal pero parece funcionar. En tu ejemplo, algo como:

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

Definitivamente estaría interesado en encontrar una mejor opción.


El mensaje de excepción se refiere a una variable de "feed", que no está presente en su código de muestra, es probable que sea causada por algo que no nos ha mostrado.

Además, tu prueba está probando Spring y tu propio código. ¿Es esto realmente lo que quieres hacer?

Es mejor suponer que Spring funciona (y lo hace), y simplemente pruebe su propia clase, es decir, llame a MyController.doSomething() directamente. Ese es uno de los beneficios del enfoque de anotación: no necesita usar solicitudes y respuestas simuladas, solo usa POJOs de dominio.


Llamaría a lo que busca después de una prueba de integración basada en la terminología del manual de referencia de Spring. ¿Qué tal si haces algo como esto?

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

Para obtener más información, he escrito una entrada de blog sobre pruebas de integración de anotaciones Spring MVC .


No estoy seguro de que mi respuesta original ayude con @PathVariable. Acabo de probar probando un @PathVariable y obtengo la siguiente excepción:

org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: error al invocar el método del controlador [public org.springframework.web.servlet.ModelAndView test.MyClass.myMethod (test.SomeType)]; la excepción anidada es java.lang.IllegalStateException: no se pudo encontrar @PathVariable [parameterName] en @RequestMapping

La razón es que las variables de ruta en la solicitud son analizadas por un interceptor. El siguiente enfoque funciona para mí:

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

Agregué una nueva publicación de blog sobre pruebas de integración de anotaciones de mvc de primavera