J'ai un simple contrôleur annoté similaire à celui-ci:
@Controller
public class MyController {
@RequestMapping("/{id}.html")
public String doSomething(@PathVariable String 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 () lève 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 printemps 3.2, il existe un moyen approprié de tester cela, d'une manière élégante et facile. Vous pourrez faire des choses comme ça:
@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 Spring. Que diriez-vous de faire quelque chose comme:
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
}
}
Pour plus d'informations, j'ai écrit un entrée de blog sur les tests d'intégration des annotations Spring MVC .
Un cadre prometteur pour tester Spring MVC https://github.com/SpringSource/spring-test-mvc
Ici, je suggère une fusion des réponses d'Emil et de scarba05 en utilisant spring-test et non spring-test-mvc. Veuillez ignorer cette réponse et vous référer aux exemples spring-test-mvc si vous utilisez Spring 3.2.x ou une version ultérieure
@Controller
public class MyControllerWithParameter {
@RequestMapping("/testUrl/{pathVar}/some.html")
public String passOnePathVar(@PathVariable String pathVar, ModelMap model){
model.addAttribute("SomeModelAttribute",pathVar);
return "viewName";
}
}
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");
}
}
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 causée par quelque chose que vous ne nous avez pas montré.
De plus, votre test teste Spring et votre propre code. Est-ce vraiment ce que tu veux faire?
Il est préférable de supposer que Spring fonctionne (ce qu'il fait) et de tester simplement votre propre classe, c'est-à-dire d'appeler MyController.doSomething()
directement. C'est l'un des avantages de l'approche d'annotation - vous n'avez pas besoin d'utiliser de fausses demandes et réponses, vous utilisez simplement des POJO de domaine.
J'ai constaté que vous pouvez insérer manuellement un mappage PathVariable dans l'objet de demande. 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<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
}
Je serais certainement intéressé à trouver une meilleure option.
Je ne suis pas sûr que ma réponse originale 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: échec de l'appel de la méthode du gestionnaire [public org.springframework.web.servlet.ModelAndView test.MyClass.myMethod (test.SomeType)]; l'exception imbriquée est Java.lang.IllegalStateException: impossible de trouver @PathVariable [parameterName] 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 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
}
J'ai ajouté un nouveau billet de blog sur test d'intégration des annotations mvc de printemps