通过在普通的servlet上下文之外实例化对象,我能够在Spring MVC控制器上进行几乎所有的单元测试。但是我希望能够运行一些测试以确保我的对象序列化正常工作,生成标题等等。
要在servlet上下文中运行测试,我创建一个修改后的上下文文件,以便不构造各种bean,然后在我的测试用例中使用EasyMock创建这些bean的模拟版本。然后我用这样的代码调用我的处理程序,并从MockHttpServletResponse获得我需要的大部分内容。
我认为这得到了它的本质:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:root-context.xml",
"file:junit-servlet-context.xml" } )
public class HomeControllerConfigHandlerHttp {
@Autowired
private RequestMappingHandlerAdapter handlerAdapter;
@Autowired
private RequestMappingHandlerMapping handlerMapping;
... //miscellaneous setup
public void someTest() throws Exception
{
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("GET");
request.setRequestURI("/config");
request.addParameter("foo", "bar");
request.addParameter("device", "oakmont");
MockHttpServletResponse response = new MockHttpServletResponse();
Object handler = handlerMapping.getHandler(request).getHandler();
replay(dblient);
expect(serviceClient.checkDevice("oakmont")).andReturn( true );
serviceClient.destroy();
replay(serviceClient);
ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);
String content = new String( response.getContentAsByteArray() );
Assert.assertEquals(content, "Expected configuration");
String content_type = response.getHeader("Content-type");
Assert.assertEquals( content_type, "text/plain");
int status = response.getStatus();
Assert.assertEquals(status, 200 );
这是我期望它做的,但有一个问题。我在我的控制器中使用@ExceptionHandler进行了大量的错误处理。这是一种在任何处理程序中退出错误情况的简单方法,它为我提供了一种暴露错误的一致方法。
@ExceptionHandler在正常的servlet部署中工作正常,但在这个单元测试模型中,当我抛出异常时它不会被调用。进入Spring代码对我来说是一个挑战,我是新手,所以我很快就迷路了。但是,在普通的servlet环境中,有一个异常处理程序可以查找带注释的处理程序。在SpringJUnit4ClassRunner下运行时,异常的处理方式不同。
如果有办法解决这个问题,我想这样做。由于缺乏先锋精神,我已经避免了spring-test-mvc,但是如果有人告诉我它可以很好地管理它,我会尝试相反。
我的junit-servlet-context.xml文件的内容几乎与Spring Template MVC向导创建的servlet-context.xml文件相同。唯一的区别是添加了一个exclude-filter,用于防止@Component的实例化,该@Component会创建我的控制器使用的一些单例。
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.cisco.onplus.home.dmz" >
<context:exclude-filter type="regex" expression=".*InitDatabase.*"/>
</context:component-scan>
</beans:beans>
答案 0 :(得分:3)
在上下文中添加以下用于加载Dispatcher servlet的类:
public class MockWebApplicationContext extends AbstractContextLoader {
@Override
public ApplicationContext loadContext(MergedContextConfiguration mergedContextConfiguration) throws Exception {
String[] locations = mergedContextConfiguration.getLocations();
return loadContext(locations);
}
@Override
public ApplicationContext loadContext(String... locations) throws Exception {
XmlWebApplicationContext webApplicationContext = new XmlWebApplicationContext();
webApplicationContext.setConfigLocations(locations);
webApplicationContext.setServletContext(new MockServletContext(new FileSystemResourceLoader() ) );
ServletConfig config = new MockServletConfig();
config.getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, webApplicationContext);
final DispatcherServlet servlet = new DispatcherServlet(webApplicationContext);
webApplicationContext.addBeanFactoryPostProcessor(new BeanFactoryPostProcessor() {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.registerResolvableDependency(DispatcherServlet.class, servlet);
}
});
webApplicationContext.refresh();
servlet.init(config);
return webApplicationContext;
}
@Override
protected String getResourceSuffix() {
return ".xml";
}
}
完成该操作后
@ContextConfiguration(locations = {"classpath:app-config.xml",loader = MockWebApplicationContext.class)
使用测试类上的Autowired批注加载DispatcherServlet。使用
处理它servlet.service(request,response);
现在它也应该处理异常流程。
虽然可能需要3.1.2。
答案 1 :(得分:1)
我恐怕你必须要看spring-test-mvc,原因是从控制器处理异常并使用ExceptionResolver调用相应的@ExceptionHandler是在DispatcherServlet
级别完成的,而不是HandlerAdapter
。您所进行的测试从HandlerAdapter
开始。
我强烈建议使用spring-test-mvc,我已经使用了一段时间,并且没有看到任何问题 - http://biju-allandsundry.blogspot.com/2012/07/spring-mvc-integration-tests.html。
对于异常流程的测试,使用spring-test-mvc:
xmlConfigSetup("classpath:/META-INF/spring/web/webmvc-config.xml")
.configureWebAppRootDir("src/main/webapp", false).build()
.perform(get("/contexts/exception"))
.andExpect(status().isOk())
.andExpect(view().name("exceptionPage"));