Testing Spring MVC Router with MockMVC

AAA picture AAA · Jan 7, 2014 · Viewed 26.3k times · Source

I'm trying to test my Spring MVC webapp with Spring test. It uses springmvc-router for routing and that appears to break the tests, which work fine when I use @RequestMapping instead of my routes.conf file.

I have a .jsp file called valid.jsp, and it displays fine when I run the development site from Jetty. The controller is:

@Controller
@EnableWebMvc
public class AuthController {
  public String valid() {
    return "valid";
  }
}

My routes.conf file maps GET /valid authController.valid.

Now, my tester looks like

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/test-context.xml",
    "/spring/spring-security.xml",
    "file:src/main/webapp/WEB-INF/mvc-config.xml"})
@WebAppConfiguration
@Import(RouteConfig.class)
public class AuthControllerTest {
  private MockMvc mockMvc;

  @Autowired
  private WebApplicationContext webApplicationContext;

  @Autowired
  private AuthenticationManager authenticationManager;

  @Before
  public void init() {
    MockitoAnnotations.initMocks(this);
    mockMvc =
        MockMvcBuilders.webAppContextSetup(webApplicationContext).dispatchOptions(true).build();
  }

  @Test
  public void testValid() throws Exception {
    mockMvc.perform(get("/validation-success"))
        .andDo(print())
        .andExpect(status().isOk());
  }

When the mockMvc.perform() is run, a NullPointerException is thrown:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:822)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:807)
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:64)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:770)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:170)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:137)
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:141)
    at com.mypackage.AuthControllerTest.testValid(AuthControllerTest.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.NullPointerException
    at org.resthub.web.springmvc.router.HTTPRequestAdapter.parseRequest(HTTPRequestAdapter.java:196)
    at org.resthub.web.springmvc.router.RouterHandlerMapping.getHandlerInternal(RouterHandlerMapping.java:166)
    at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:300)
    at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1101)
    at org.springframework.test.web.servlet.TestDispatcherServlet.getHandler(TestDispatcherServlet.java:104)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:916)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:931)
    ... 41 more

So the question is: is it possible to use Spring's test framework with springmvc-router?

Answer

TheMasster12 picture TheMasster12 · Jan 8, 2014

After doing some digging, I've discovered that the HTTPRequestAdapter.parseRequest() method has an issue with the way that MockMVC sends requests. Specifically, the request sent by MockMVC doesn't include a header in the request with the name host.

HTTPRequestAdapter requires that header and does not account for the fact that it can be null, so it generates the NullPointerException.

I fixed the issue with this code:

mockMvc.perform(get("/validation-success")
       .header("host", "localhost:80"))
       .andExpect(status().isOk());

The host header won't be null and your tests should pass.