Geo*_*ava 5 java spring spring-mvc spring-test mockmvc
我正在尝试进行测试以涵盖登录功能。Spring 的版本是 3.2.12。我有一个会话 bean,声明为:
@Service
@Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
public class ClientSessionServiceImpl implements ClientSessionService {
@Autowired
private HttpServletRequest request;
// This method is called during the login routine from the filter
public boolean checkUser() {
// I rely on request attributes here, which were set in the filter
}
Run Code Online (Sandbox Code Playgroud)
这在服务器上运行时效果很好,但是当使用spring-test的方式运行时,问题就来了。这是我的测试方法:
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(springSecurityFilterChain).build();
mockMvc.perform(post(URL));
Run Code Online (Sandbox Code Playgroud)
经过调试,我发现,当测试 spring 上下文启动时,在 ServletTestExecutionListener.setUpRequestContextIfNecessary 中创建了一个 MockHttpServletRequest 实例,MockHttpServletRequest request = new MockHttpServletRequest(mockServletContext);? // 让我们称这个实例为 A。这是到处注入的实例,我使用
@Autowired
HttpServletRequest request;
Run Code Online (Sandbox Code Playgroud)
而调用 MockMvc.perform 会创建另一个 MockHttpServletRequest 实例(我们称其为实例 B),该实例被传递给所有过滤器、servlet 等。因此,基本上,我在请求中的过滤器中设置的属性不能在 ClientSessionServiceImpl 中读取,因为在那里注入了不同的 MockHttpServletRequest 实例。
我花了很多时间在这上面,但仍然没有找到解决方案。
PS 我通过 StackOverflow 搜索,有类似标题的问题,但描述的问题与我的不同,因为我不想将 HttpServletRequest 作为参数传递,并且宁愿让它自动装配,除非有充分的理由它。
因此,基本上,我在请求的过滤器中设置的属性无法在 ClientSessionServiceImpl 中读取,因为那里注入了 MockHttpServletRequest 的不同实例。
这是一个关于 Spring 何时RequestAttributes填充到RequestContextHolder. 在生产中,我假设您正在配置 aRequestContextFilter或 a RequestContextListener。
无论如何,RequestContextFilter在测试中手动将 的实例添加到过滤器链的前面将解决问题。
mockMvc = MockMvcBuilders
.webAppContextSetup(this.wac)
.addFilters(new RequestContextFilter(), testFilterChain)
.build();
Run Code Online (Sandbox Code Playgroud)
请注意,这将成为 Spring Framework 4.2 中的默认行为:模拟的代码RequestContextFilter将直接在MockMvc. 有关详细信息,请参阅 JIRA 问题SPR-13217。
顺便说一句,不支持配置MockHttpServletRequest由 创建的。ServletTestExecutionListener如果您正在使用MockMvc,则需要通过RequestBuilders.
然而,话虽如此,如果您确实需要修改ServletTestExecutionListener手动创建的模拟请求,然后将其重新使用MockMvc,则可以在项目中创建以下类:
package org.springframework.test.web.servlet.request;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.HttpMethod;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* Patched version of {@link MockHttpServletRequestBuilder}.
*
* @author Sam Brannen
* @since 4.2
*/
public class PatchedMockHttpServletRequestBuilder extends MockHttpServletRequestBuilder {
public static MockHttpServletRequestBuilder get(String urlTemplate, Object... urlVariables) {
return new PatchedMockHttpServletRequestBuilder(HttpMethod.GET, urlTemplate, urlVariables);
}
public PatchedMockHttpServletRequestBuilder(HttpMethod httpMethod, String urlTemplate, Object... urlVariables) {
super(httpMethod, urlTemplate, urlVariables);
}
/**
* Create a {@link MockHttpServletRequest}.
* <p>If an instance of {@code MockHttpServletRequest} is available via
* the {@link RequestAttributes} bound to the current thread in
* {@link RequestContextHolder}, this method simply returns that instance.
* <p>Otherwise, this method creates a new {@code MockHttpServletRequest}
* based on the supplied {@link ServletContext}.
* <p>Can be overridden in subclasses.
* @see RequestContextHolder#getRequestAttributes()
* @see ServletRequestAttributes
*/
@Override
protected MockHttpServletRequest createServletRequest(ServletContext servletContext) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes) {
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
if (request instanceof MockHttpServletRequest) {
return (MockHttpServletRequest) request;
}
}
return new MockHttpServletRequest(servletContext);
}
}
Run Code Online (Sandbox Code Playgroud)
注意:必须在org.springframework.test.web.servlet.request包装内;否则,它无法扩展MockHttpServletRequestBuilder所需的内容。
然后,使用get()fromPatchedMockHttpServletRequestBuilder而不是 from方法MockMvcRequestBuilders,一切都应该按您的预期工作!
显然,上面的示例重新实现了 get(),但是您自然可以对 等执行相同的操作post()。
仅供参考:我最终可能会将上述修补版本提交createServletRequest()到 Spring Framework 4.2.x(请参阅 JIRA 问题SPR-13211)。
问候,
Sam(Spring TestContext 框架的作者)
| 归档时间: |
|
| 查看次数: |
13063 次 |
| 最近记录: |