Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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..

like image 478
Pete Avatar asked Jun 15 '12 07:06

Pete


2 Answers

@Biju -- I appreciate this answer and it saved me a trip, and I'm not trying to shoot the messenger here, but I must say for the benefit of anyone on the Spring team who might be motivated to build something better, I'm finding MockMVC to be pretty much an exercise in triviality and futility. First, Spring authentication is not directly supported. OK fair enough if you scout StackOverflow you can find a workaround to that. Then on my own I found that if you have in your spring context configuration, every path you can make up comes back as "OK" even if it should return "notFound". OK, whatever, take that out and let us never speak of it again. :) And now come to find out that MockMVC is really only MockMC -- no view processing takes place. So at the end of the day what this software is good for is testing applications that contain neither security nor views -- which would be, what, exactly, toy JSON applications?

And this is not really an answer, it's a rant, which means now MockMVC is going to cost me StackOverflow reputation too! :) Hulk smash!

[EDIT] -- OK rant aside it does look like there are ways around this. [LATER EDIT] Unfortunately the way around this I found is no longer available.

like image 141
John Lockwood Avatar answered Nov 15 '22 19:11

John Lockwood


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.

like image 31
Biju Kunjummen Avatar answered Nov 15 '22 20:11

Biju Kunjummen