简体   繁体   English

Spring-Test-MVC / MockServletContext - 测试中的内容为空,但在Tomcat上工作

[英]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. 我们正在尝试为Spring-MVC Web应用程序设置Spring-Test-MVC。 We started out using freemarker and everything was fine. 我们开始使用freemarker,一切都很好。 We decided against it though and are now trying to set it up with JSP. 我们决定反对它,现在正试图用JSP进行设置。 When the test app is deployed on a Tomcat it's working. 当测试应用程序部署在Tomcat上时,它正在运行。 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. 它说: content type not set或如果删除,内容将为空。 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: 这是我们的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: WebContextLoader:

public class WebContextLoader extends GenericWebContextLoader {
    public WebContextLoader() {
        super("src/main/webapp", false);
    }
}

GenericWebContextLoader is the original by spring-test-mvc. GenericWebContextLoader是spring-test-mvc的原始版本。

The MockMvc gets setup as a Bean like this: MockMvc像这样设置为Bean:

@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. 测试框架不使用web.xml,因为它之前的工作并不重要。

I'm thinking there must be an additional setup in the servlet-context. 我想在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. 它被加载了,我检查了但是对于Tomcat部署的应用程序而言重要的是,我为前缀和后缀设置它将被测试忽略。

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. 顺便说一句:不想再这样了,所以我跳过了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... 我们正在使用Spring 3.1,spring-test-mvc 1.0.0..BUILD-SNAPSHOT,jsp-ap 2.2,jstl 1.2,...如果您需要了解更多,我会尝试将其上传到某个地方......


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 EDIT2

Used the print method with the following output: 使用具有以下输出的print方法:

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 EDIT3

Just found out the following: 刚发现以下内容:

JSP requires a servlet container. JSP需要一个servlet容器。 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.. 所以我似乎无法以这种方式测试我的页面...如果有人知道如何解决这个问题,请让我知道..

@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. @Biju - 我很欣赏这个答案,它让我省去了一次旅行,而且我不是想在这里拍摄信使,但我必须说出春天团队中任何一个可能有动力去建立更好的东西的人的利益,我我发现MockMVC几乎是一种琐碎无用的练习。 First, Spring authentication is not directly supported. 首先,不直接支持Spring身份验证。 OK fair enough if you scout StackOverflow you can find a workaround to that. 如果你侦察StackOverflow,你可以找到一个解决方法。 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”,即使它应该返回“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. :)现在来发现MockMVC实际上只是MockMC - 没有视图处理发生。 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? 因此,在一天结束时,这个软件最适合的是测试既不包含安全性也不包含视图的应用程序 - 这将是什么,确切地说,玩具JSON应用程序是什么?

And this is not really an answer, it's a rant, which means now MockMVC is going to cost me StackOverflow reputation too! 这不是一个真正的答案,这是一个咆哮,这意味着现在MockMVC将花费我StackOverflow的声誉! :) 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. [LATER EDIT]不幸的是我找到的方式不再可用了。

Adding onto your edit3, essentially for JSP rendering the final call is 添加到您的edit3,主要是为JSP呈现最终调用

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). 并且RequestDispatcher实现由容器提供(因为它取决于如何编译jsp,在何处放置已编译的jsp等)。 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. RequestDispatcher的Mock实现只是捕获转发的JSP页面,您只能验证JSP的路径是否符合您的预期。

I have created a modified version of MockRequestDispatcher that builds the same chain as MockMvc and passes the request to that chain for forward requests. 我创建了一个MockRequestDispatcher的修改版本,它与MockMvc构建相同的链,并将请求传递给该链以用于转发请求。 That fixes this issue for me. 这为我解决了这个问题。 Some more work has to be done if the view is rendered outside of the dispatcher servlet (eg via jsp). 如果视图在调度程序servlet之外呈现(例如通过jsp),则必须完成更多工作。

The code is there: https://gist.github.com/micw/a8b20720db900762a29d 代码在那里: https//gist.github.com/micw/a8b20720db900762a29d

Most work was to inject it at the right place. 大多数工作是在正确的地方注入它。 I did this my creating a RequestPostProcessor for MockMvc and some mockito-magic that intercepts the MockHttpServletRequest.getRequestDispatcher call. 我这样做了我为MockMvc创建一个RequestPostProcessor和一些拦截MockHttpServletRequest.getRequestDispatcher调用的mockito-magic。

To use it, add it to your test classpath and call it directly after creating your MockMvc instance: 要使用它,请将其添加到测试类路径并在创建MockMvc实例后直接调用它:

    mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
            [...]
            .build();
    WebMvcRequestDispatcherForwardFix.apply(mvc);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM