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

我们正在尝试为Spring-MVC Web应用程序设置Spring-Test-MVC。 我们开始使用freemarker,一切都很好。 我们决定反对它,现在正试图用JSP进行设置。 当测试应用程序部署在Tomcat上时,它正在运行。 当我们运行简单测试时:

@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!"))); } 

它说: content type not set或如果删除,内容将为空。 然而,控制器将被调用,因此映射必须起作用。

所以这强烈暗示我们的测试没有呈现视图,但我不知道我可能缺少什么设置。

这是我们的servlet-context.xml:

         

WebContextLoader:

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

GenericWebContextLoader是spring-test-mvc的原始版本。

MockMvc像这样设置为Bean:

 @Configuration public class TestConfig { @Inject private WebApplicationContext wac; @Bean public MockMvc create(){ return (MockMvcBuilders.webApplicationContextSetup(this.wac).build()); } } 

这就是设置。 测试框架不使用web.xml,因为它以前工作无关紧要。

我想在servlet-context中必须有一个额外的设置。 它被加载了,我检查了但是对于Tomcat部署的应用程序而言,重要的是,我为前缀和后缀设置它将被测试忽略。

不确定错误跟踪有多大帮助,但这里是:

 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) 

并且测试输出:

 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 

所以感谢任何有助于我找到问题的建议!

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


编辑

如果您需要更多信息或无法回答我的问题,请告诉我。 真的需要搞清楚,我不知道,从哪里开始。 所以也欢迎任何想法或评论。


EDIT2

使用具有以下输出的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 = [] 

这只是更好地展示了问题而不是解决方案……


EDIT3

刚发现以下内容:

JSP需要一个servlet容器。 所以我似乎无法以这种方式测试我的页面…如果有人知道如何解决这个问题,请让我知道..

添加到您的edit3,主要是为JSP呈现最终调用

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

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

@Biju – 我很欣赏这个答案,它让我省去了一次旅行,而且我不是想在这里拍摄信使,但我必须说出spring团队中任何一个可能有动力去建立更好的东西的人的利益,我我发现MockMVC几乎是一种琐碎无用的练习。 首先,不直接支持Spring身份validation。 好的,如果你侦察StackOverflow你可以找到一个解决方法。 然后我自己发现,如果你有弹簧上下文配置,你可以弥补的每个路径都会返回“OK”,即使它应该返回“notFound”。 好吧,无论如何,把它拿出去让我们再也不说它了。 :)现在来发现MockMVC实际上只是MockMC – 没有视图处理发生。 因此,在一天结束时,这个软件最适合的是测试既不包含安全性也不包含视图的应用程序 – 这将是什么,确切地说,玩具JSON应用程序是什么?

这不是一个真正的答案,这是一个咆哮,这意味着现在MockMVC将花费我StackOverflow的声誉! :) 绿巨人粉碎!

[编辑] – 好吧咆哮它看起来确实有这样的方法。 [LATER EDIT]不幸的是我找到的方式不再可用了。

我创建了一个MockRequestDispatcher的修改版本,它与MockMvc构建相同的链,并将请求传递给该链以用于转发请求。 这为我解决了这个问题。 如果视图在调度程序servlet之外呈现(例如通过jsp),则必须完成更多工作。

代码在那里: https : //gist.github.com/micw/a8b20720db900762a29d

大多数工作是在正确的地方注入它。 我这样做了我为MockMvc创建一个RequestPostProcessor和一些拦截MockHttpServletRequest.getRequestDispatcher调用的mockito-magic。

要使用它,请将其添加到测试类路径中,并在创建MockMvc实例后直接调用它:

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