在Spring 3.0.5及之前的版本中,可以创建一个基本控制器测试类,它从Spring Application Context中获取HandlerMapping对象,以通过URL直接访问和调用控制器的方法。我发现这种方法非常棒,因为除了测试控制器的方法之外,我还在我的单元/集成测试中测试路径变量等。
使用Spring 3.2.4,由于重构了Spring处理Url映射的方式,这种方法似乎不再可能。我看到Spring提供了一个新的MVC测试框架,但说实话,我认为它的设计过于冗长,看起来与框架的其余部分或我的应用程序代码完全不同。它也不能很好地与IntelliJ中的智能感知功能配合使用。说实话,我宁愿不使用它。
那么,是否有另一种方法来测试不使用新的Spring MVC测试框架的控制器URL,就像我之前做的那样?我有一个371控制器测试的现有项目,我真的想避免迁移所有东西以使用Spring MVC测试框架。
这是我用来测试使用Spring 3.0.5的控制器的handle()
方法:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
final HandlerInterceptor[] interceptors = handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
}
protected ModelAndView handle(String method, String path, String queryString) throws Exception {
request.setMethod(method);
request.setRequestURI(path);
if(queryString != null) {
String[] parameters = queryString.split("&");
for(String parameter : parameters) {
String[] pair = parameter.split("=");
if(pair.length == 2) {
request.setParameter(pair[0], pair[1]);
} else {
request.setParameter(pair[0], "");
}
}
}
return handle(request, response);
}
protected ModelAndView handle(String method, String path, String attribute, Object object) throws Exception {
MockHttpSession session = new MockHttpSession();
session.setAttribute(attribute, object);
request.setSession(session);
return handle(method, path, null);
}
protected ModelAndView handle(String method, String path) throws Exception {
return handle(method, path, null);
}
以下是一些测试代码,说明了我如何使用handle()
方法:
@Test
public void show() throws Exception {
ModelAndView modelAndView = handle("GET", "/courseVersion/1/section/1");
Section section = (Section) modelAndView.getModel().get("section");
assertEquals(1, section.getId());
}
这是我的servlet应用程序上下文:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:applicationContext.properties"/>
</bean>
<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
</bean>
<security:global-method-security pre-post-annotations="enabled">
<security:expression-handler ref="expressionHandler"/>
</security:global-method-security>
<context:component-scan base-package="keiko.web.controllers"/>
<mvc:annotation-driven validator="validator" />
<mvc:interceptors>
<bean class="keiko.web.interceptors.IpValidationInterceptor" />
<bean class="keiko.web.interceptors.UnreadMessagesInterceptor" />
<bean class="keiko.web.interceptors.ThemeInterceptor" />
<bean class="keiko.web.interceptors.ApplicationMenuInterceptor" />
</mvc:interceptors>
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="freemarkerSettings">
<props>
<prop key="auto_import">lib/common.ftl as common, lib/layouts.ftl as layouts</prop>
<prop key="whitespace_stripping">true</prop>
</props>
</property>
<property name="freemarkerVariables">
<map>
<entry key="template_update_delay" value="0"/>
<entry key="default_encoding" value="ISO-8859-1"/>
<entry key="number_format" value="0.##"/>
<entry key="xml_escape">
<bean class="freemarker.template.utility.XmlEscape"/>
</entry>
</map>
</property>
</bean>
<bean id="contentNegotiatingViewResolver"
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="1"/>
<property name="ignoreAcceptHeader" value="true" />
<property name="defaultContentType" value="text/html" />
<property name="mediaTypes">
<map>
<entry key="html" value="text/html"/>
<entry key="json" value="application/json"/>
</map>
</property>
<property name="useNotAcceptableStatusCode" value="true" />
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="contentType" value="text/html" />
<property name="order" value="2"/>
<property name="cache" value="${freemarker.cache}"/>
<property name="prefix" value=""/>
<property name="suffix" value=".ftl"/>
<property name="exposeSpringMacroHelpers" value="true"/>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<property name="contentType" value="application/json" />
</bean>
</list>
</property>
</bean>
<bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="1000000"/>
</bean>
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="keiko.domain.courseauthor.SectionIsDelayedException">error/sectionIsDelayed</prop>
<prop key="keiko.service.director.CompanyHomepageClosedException">error/registrationClosed</prop>
<prop key="keiko.service.director.IpDeniedException">error/ipDenied</prop>
</props>
</property>
</bean>
</beans>
答案 0 :(得分:1)
基本上你的测试方法从一开始就存在缺陷。总有可能存在多于1个HandlerMapping
和1个HandlerAdapter。你基本上做的是模仿DispatcherServlet。
您应该做的是查找所有HandlerMapping
和HandlerAdapter
并检查其中一个是否与该网址匹配(即返回HandlerExecutionChain)并选择相应的HandlerAdapter
(调用supports
方法)。你在做什么基本上是DispatcherServlet
正在做的事情。