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 文件几乎相同。唯一的区别是添加了一个排除过滤器,该过滤器用于防止 @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>
4

2 回答 2

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。

于 2012-10-25T18:52:50.653 回答
1

恐怕您将不得不查看spring-test-mvc,原因是来自控制器的异常处理和使用 ExceptionResolver 调用适当的@ExceptionHandler 是在级别上完成的DispatcherServlet,而不是HandlerAdapter。您的测试从HandlerAdapter.

我强烈推荐 spring-test-mvc,我已经使用了一段时间,并没有看到我的场景有任何问题 - http://biju-allandsundry.blogspot.com/2012/07/spring-mvc-integration-测试.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"));
于 2012-07-25T14:14:50.947 回答