Spring-Test-MVC / MockServletContext - content empty in test but working on Tomcat

java, mocking, servlets, spring-mvc, testing

Solution

Adding onto your edit3, essentially for JSP rendering the final call is

RequestDispatcher requestDispatcher = httpRequest.getRequestDispacher(jspPath)
requestDispatcher.forward(httpRequest,httpResponse)

and the `RequestDispatcher` implementations are provided by the container (since it is dependent on how the jsp's need to be compiled, where to place the compiled jsp's etc). The Mock implementation of RequestDispatcher simply captures the forwarded JSP page, and you can only validate that the path to the JSP is what you expect it to be.

Problem

We're trying to set up Spring-Test-MVC for our Spring-MVC web app. We started out using freemarker and everything was fine. We decided against it though and are now trying to set it up with JSP. When the test app is deployed on a Tomcat it's working. When we run the simple test: ``` @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(loader = WebContextLoader.class, locations = { "file:src/main/webapp/WEB-INF/servlet-context.xml" }) public class SkelletonTest { @Inject private MockMvc mockMvc; @Test public void homeTest() throws Exception { mockMvc.perform(get("/")).andExpect(status().isOk()) .andExpect(content().type("text/html;charset=ISO-8859-1")) .andExpect(content().string(containsString("Hello World!"))); } ``` it says: `content type not set` or if that is removed, the content will just be empty. The controller will get called however, so the mapping must work. So this strongly suggests that the view is not rendered for our tests but I have no clue what setup I might be missing. Here's our servlet-context.xml: ``` <context:component-scan base-package="package.to.controllers" /> <mvc:annotation-driven /> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="exposeContextBeansAsAttributes" value="true" /> <property name="prefix" value="/views/" /> <property name="suffix" value=".jsp" /> </bean> ``` The WebContextLoader: ``` public class WebContextLoader extends GenericWebContextLoader { public WebContextLoader() { super("src/main/webapp", false); } } ``` GenericWebContextLoader is the original by spring-test-mvc. The MockMvc gets setup as a Bean like this: ``` @Configuration public class TestConfig { @Inject private WebApplicationContext wac; @Bean public MockMvc create(){ return (MockMvcBuilders.webApplicationContextSetup(this.wac).build()); } } ``` So that's the setup. web.xml is not used by the test framework and shouldn't matter as it was working before. I'm thinking there must be an additional setup in the servlet-context. It get's loaded, that I checked but while for the Tomcat deployed app it matters, what I set for prefix and suffix it will just get ignored by the test. Not sure how much the error trace will help, but here it is: ``` java.lang.AssertionError: Content type not set at org.springframework.test.web.AssertionErrors.fail(AssertionErrors.java:35) at org.springframework.test.web.AssertionErrors.assertTrue(AssertionErrors.java:57) at org.springframework.test.web.server.result.ContentResultMatchers$1.match(ContentResultMatchers.java:59) at org.springframework.test.web.server.MockMvc$1.andExpect(MockMvc.java:84) at our.package.SkelletonTest.homeTest(SkelletonTest.java:30) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) 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:231) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) 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:300) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) ``` And the test output: ``` 2012-06-15 10:41:04 TestContextManager [INFO] @TestExecutionListeners is not present for class [class package.to.test.SkelletonTest]: using defaults. 2012-06-15 10:41:05 XmlBeanDefinitionReader [INFO] Loading XML bean definitions from URL [file:src/main/webapp/WEB-INF/servlet-context.xml] 2012-06-15 10:41:05 ClassPathBeanDefinitionScanner [INFO] JSR-330 'javax.inject.Named' annotation found and supported for component scanning 2012-06-15 10:41:05 GenericWebApplicationContext [INFO] Refreshing org.springframework.web.context.support.GenericWebApplicationContext@158539f: startup date [Fri Jun 15 10:41:05 CEST 2012]; root of context hierarchy 2012-06-15 10:41:05 AutowiredAnnotationBeanPostProcessor [INFO] JSR-330 'javax.inject.Inject' annotation found and supported for autowiring 2012-06-15 10:41:05 DefaultListableBeanFactory [INFO] Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c64bc2: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testConfig,freemarkerController,homeController,tableService,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0,org.springframework.format.support.FormattingConversionServiceFactoryBean#0,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0,org.springframework.web.servlet.handler.MappedInterceptor#0,org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0,org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0,org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,viewResolver,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0,create]; root of factory hierarchy 2012-06-15 10:41:05 RequestMappingHandlerMapping [INFO] Mapped "{[/],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.web.servlet.ModelAndView package.to.controller.HomeController.index() 2012-06-15 10:41:05 RequestMappingHandlerMapping [INFO] Mapped "{[/test],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String package.to.controller.HomeController.test(org.springframework.ui.Model) 2012-06-15 10:41:06 GenericWebContextLoader$1 [INFO] Initializing Spring FrameworkServlet '' 2012-06-15 10:41:06 TestDispatcherServlet [INFO] FrameworkServlet '': initialization started 2012-06-15 10:41:06 TestDispatcherServlet [INFO] FrameworkServlet '': initialization completed in 32 ms 2012-06-15 10:41:06 GenericWebApplicationContext [INFO] Closing org.springframework.web.context.support.GenericWebApplicationContext@158539f: startup date [Fri Jun 15 10:41:05 CEST 2012]; root of context hierarchy 2012-06-15 10:41:06 DefaultListableBeanFactory [INFO] Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@c64bc2: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,testConfig,freemarkerController,homeController,tableService,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0,org.springframework.format.support.FormattingConversionServiceFactoryBean#0,org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#0,org.springframework.web.servlet.handler.MappedInterceptor#0,org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0,org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver#0,org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver#0,org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,viewResolver,org.springframework.context.annotation.ConfigurationClassPostProcessor$ImportAwareBeanPostProcessor#0,create]; root of factory hierarchy ``` So thank's for any suggestions that help me locate the problem! Btw: Didn't want this to get any longer, so I skipped the pom. We're using Spring 3.1, spring-test-mvc 1.0.0..BUILD-SNAPSHOT, jsp-ap 2.2, jstl 1.2, ... If you need to know more, I'll try to upload it somewhere... Edit Please let me know, if you need more information or why you can't answer my question. Really need to figure it out and I have no idea, where to start. So any thoughts or comments are welcome too. Edit2 Used the print method with the following output: ``` MockHttpServletRequest: HTTP Method = GET Request URI = / Parameters = {} Headers = {} Handler: Type = package.to.controller.HomeController Method = public org.springframework.web.servlet.ModelAndView package.to.controller.HomeController.index() Resolved Exception: Type = null ModelAndView: View name = index View = null Attribute = welcome value = Hello World! FlashMap: MockHttpServletResponse: Status = 200 Error message = null Headers = {} Content type = null Body = Forwarded URL = /views/index.jsp Redirected URL = null Cookies = [] ``` Which just shows the problem better but not the solution... edit3 Just found out the following: JSP requires a servlet container. So it seems I can't test my pages this way... If anyone has any idea how to work around this problem, please let me know..

Original source