Wie testen Einheit ein Spring MVC-Controller @PathVariable mit?
-
05-07-2019 - |
Frage
Ich habe einen einfachen kommentierten Controller ähnlich wie diese:
@Controller
public class MyController {
@RequestMapping("/{id}.html")
public String doSomething(@PathVariable String id, Model model) {
// do something
return "view";
}
}
und ich möchte es mit einem Unit-Test testen, wie folgt aus:
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
}
}
Das Problem ist, dass AnnotationMethodHandlerAdapter.handler () -Methode eine Ausnahme auslöst:
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)
Lösung
Wie von Spring 3.2 gibt es eine richtige Art und Weise dies zu testen, in einem eleganten und einfachen Weg. Sie können Dinge wie dies zu tun:
@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"));
}
}
Andere Tipps
Ich würde nennen, was Sie nach einem Integrationstest auf der Terminologie im Frühjahr Referenzhandbuch basiert sind. Wie wäre es, etwas zu tun wie:
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
}
}
Weitere Informationen Ich habe eine Blog-Eintrag über Integrationstests Spring MVC Anmerkungen .
Ein vielversprechender Rahmen für die Prüfung Spring MVC https://github.com/SpringSource/spring-test-mvc
Die Ausnahmemeldung bezieht sich auf eine „feed“ Variable, die nicht in Ihrem Beispielcode ist, dann ist es wahrscheinlich, durch etwas verursacht wird haben Sie uns nicht gezeigt.
Auch Ihr Test testet Frühling und Ihren eigenen Code. Ist das wirklich das, was Sie tun möchten?
Es ist besser, dass der Frühling Werke zu übernehmen (was es tut), und nur Ihre eigene Klasse testen, das heißt Anruf MyController.doSomething()
direkt. Das ist ein Vorteil der Anmerkung Ansatz -. Sie müssen nicht Mock Anfragen und Antworten verwenden, die Sie gerade Domain POJOs verwenden
Vorausgesetzt, Sie Frühlings-3.0.x verwenden.
Hier schlage ich eine Fusion von Emil und scarba05 Antworten mit Feder-Test nicht feder Test-mvc. Bitte überspringen Sie diese Antwort und beziehen sich auf Feder Test-mvc Beispiele, wenn Sie Frühlings-3.2.x oder höher verwenden
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");
}
}
Ich habe festgestellt, dass Sie manuell ein PathVariable Mapping in das Request-Objekt einfügen. Dies ist deutlich nicht ideal, aber scheint zu funktionieren. In Ihrem Beispiel so etwas wie:
@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
}
ich auf jeden Fall interessiert wäre eine bessere Wahl bei der Suche nach.
Ich bin nicht sicher, ob meine ursprüngliche Antwort mit @PathVariable helfen wird. Ich habe gerade versucht, eine @PathVariable testen und ich erhalte die folgende Ausnahme:
org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: Fehler-Handler-Methode [public org.springframework.web.servlet.ModelAndView test.MyClass.myMethod (test.SomeType)] aufzurufen; verschachtelte Ausnahme ist java.lang.IllegalStateException: Kann nicht finden @PathVariable [parameter] in @RequestMapping
Der Grund dafür ist, dass die Pfadvariablen in der Anforderung durch einen Abfangjäger analysiert zu bekommen. Der folgende Ansatz funktioniert gut für mich:
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
}
Ich habe einen neuen Blog-Eintrag auf Integrationstests Spring MVC Annotationen