@ExceptionHandler不使用Spring MVC 3.1单元测试

时间:2012-07-25 11:50:08

标签: spring unit-testing spring-mvc spring-3

通过在普通的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>

2 个答案:

答案 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"));