Как модульное тестирование контроллера Spring MVC с помощью @PathVariable?
у меня есть простой аннотированный контроллер, похожий на этот:
@Controller
public class MyController {
@RequestMapping("/{id}.html")
public String doSomething(@PathVariable String id, Model model) {
// do something
return "view";
}
}
и я хочу проверить его с помощью модульного теста, как это:
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
}
}
проблема в том, что AnnotationMethodHandlerAdapter.метод handler () вызывает исключение:
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)
7 ответов:
С весны 3.2, есть правильный способ проверить это, в элегантный и простой способ. Вы сможете делать такие вещи, как это:
@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")); } }для получения дополнительной информации, взгляните на http://blog.springsource.org/2012/11/12/spring-framework-3-2-rc1-spring-mvc-test-framework/
Я бы назвал то, что вы после интеграционного теста, основанного на терминологии в справочном руководстве Spring. Как насчет того, чтобы сделать что-то вроде:
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 } }для получения дополнительной информации я написал запись в блоге о тестировании интеграции Spring MVC аннотации.
перспективный фреймворк для тестирования Spring MVC https://github.com/SpringSource/spring-test-mvc
сообщение об исключении относится к переменной" feed", которая отсутствует в вашем примере кода, скорее всего, это вызвано чем-то, что Вы нам не показали.
кроме того, ваш тест тестирует Spring и свой собственный код. Это действительно то, что ты хочешь сделать?
лучше предположить, что Spring работает (что он и делает), и просто Протестируйте свой собственный класс, т. е. вызовите
MyController.doSomething()напрямую. Это одно из преимуществ подхода аннотации - вам не нужно использовать макет запросы и ответы, вы просто используете домен POJOs.
при условии, что вы используете Spring 3.0.x.
здесь я предлагаю слияние ответов Emil и scarba05 с использованием spring-test, а не spring-test-mvc. Пожалуйста, пропустите этот ответ и обратитесь к примерам spring-test-mvc, если вы используете Spring 3.2.x или позже
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"); }}
Я обнаружил, что вы можете вручную вставить PathVariable отображение в объект запроса. Это явно неидеально, но, похоже, работает. В вашем примере, что-то вроде:
@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 }Я определенно был бы заинтересован в поиске лучшего варианта.
Я не уверен, что мой оригинальный ответ поможет с @PathVariable. Я только что пробовал тестировать @PathVariable и получаю следующее исключение:
org.springframework.сеть.связывать.аннотация.поддержка.HandlerMethodInvocationException: не удалось вызвать метод обработчика [public org.springframework.сеть.сервлет.Тест ModelAndView.Класса MyClass.myMethod (тест.SomeType)]; вложенное исключение-java.ленг.IllegalStateException: не удалось найти @PathVariable [parameterName] в @RequestMapping
причина в том, что переменные пути в запросе анализируются перехватчиком. Для меня работает следующий подход:
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 }Я добавил новый пост в блоге на интеграция тестирования весна MVC аннотации
Comments