Java Spring测试集成测试中的自动连线HttpServletRequest

Java Spring测试集成测试中的自动连线HttpServletRequest,java,spring,spring-mvc,spring-test,mockmvc,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

我试图做一个测试,以涵盖登录功能。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
    }
当在服务器上运行时,这种方法可以很好地工作,但是当使用SpringTest方法运行时,问题就出现了。这是我的测试方法:

this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(springSecurityFilterChain).build();
mockMvc.perform(post(URL));
调试之后,我发现,当TestSpring上下文启动时 ServletTestExecutionListener.SetupRequestContextIf必要 创建了MockHttpServletRequest的实例, MockHttpServletRequest=新的MockHttpServletRequest(mockServletContext);
 //让我们把这个实例称为A。 这是一个到处都被注入的实例,我使用

@Autowired
HttpServletRequest request;
然而,调用MockMvc.perform会创建MockHttpServletRequest的另一个实例(我们称之为实例B),该实例会传递给所有筛选器、servlet等。因此,基本上,我在请求中的筛选器中设置的属性不能在ClientSessionServiceImpl中读取,因为MockHttpServletRequest的不同实例被注入其中

我花了很多时间在这上面,但仍然没有找到解决方案

附言。 我在StackOverflow中搜索过,有一些标题类似的问题,但描述的问题与我的不同,因为我不想将HttpServletRequest作为参数传递,而是希望它自动连接,除非有充分的理由。

您可以使用它来交换在perform中创建的请求。您可以向测试中添加一个util方法,如

private static RequestPostProcessor mockedRequest(final MockHttpServletRequest mockHttpServletRequest) {
    return new RequestPostProcessor() {
        @Override
        public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
            return mockHttpServletRequest;
        }
    };
}
您可以通过
方法添加此后处理器来应用它

    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(springSecurityFilterChain).build();
    mockMvc.perform(post(URL).with(mockedRequest(request)));
在这里,传递给
mockedRequest
方法的
request
将是包含所需属性的原始请求

因此,基本上,我在请求的过滤器中设置的属性不能 在ClientSessionServiceImpl中读取,因为 MockHttpServletRequest被注入其中

这是一个与Spring的
RequestAttributes
何时填充到
RequestContextHolder
中有关的时间问题。在生产环境中,我假设您正在配置
RequestContextFilter
RequestContextListener

在任何情况下,在测试中手动将
RequestContextFilter
的实例添加到过滤器链的前端都可以解决问题

mockMvc=MockMvcBuilders
.webAppContextSetup(this.wac)
.addFilters(新建RequestContextFilter(),testFilterChain)
.build();
请注意,这将成为SpringFramework 4.2中的默认行为:模拟
RequestContextFilter
的代码将直接在
MockMvc
中实现。详情见JIRA问题


另外,不支持配置由
ServletTestExecutionListener
创建的
MockHttpServletRequest
。如果您使用的是
MockMvc
,则应通过
RequestBuilders
配置模拟请求

但是,尽管如此,如果您确实需要手动修改由
ServletTestExecutionListener
创建的模拟请求,然后将其与
MockMvc
一起重新使用,那么您可以在项目中创建以下类:

包org.springframework.test.web.servlet.request;
导入javax.servlet.ServletContext;
导入javax.servlet.http.HttpServletRequest;
导入org.springframework.http.HttpMethod;
导入org.springframework.mock.web.MockHttpServletRequest;
导入org.springframework.web.context.request.RequestAttributes;
导入org.springframework.web.context.request.RequestContextHolder;
导入org.springframework.web.context.request.ServletRequestAttributes;
/**
*{@link MockHttpServletRequestBuilder}的修补版本。
*
*@作者萨姆·布兰宁
*@自4.2
*/
公共类PatchedMockHttpServletRequestBuilder扩展了MockHttpServletRequestBuilder{
公共静态MockHttpServletRequestBuilder获取(字符串urlTemplate、对象…urlVariables){
返回新的PatchedMockHttpServletRequestBuilder(HttpMethod.GET、urlTemplate、urlVariables);
}
公共补丁DockHttpServletRequestBuilder(HttpMethod HttpMethod、字符串urlTemplate、对象…urlVariables){
super(httpMethod、urlTemplate、urlVariables);
}
/**
*创建一个{@link MockHttpServletRequest}。
*如果{@code MockHttpServletRequest}的实例通过
*绑定到中当前线程的{@link RequestAttributes}
*{@link RequestContextHolder},此方法仅返回该实例。
*否则,此方法将创建一个新的{@code MockHttpServletRequest}
*基于提供的{@link ServletContext}。
*可以在子类中重写。
*@请参阅RequestContextHolder#getRequestAttributes()
*@请参阅ServletRequestAttributes
*/
@凌驾
受保护的MockHttpServletRequest createServletRequest(ServletContext ServletContext){
RequestAttributes RequestAttributes=RequestContextHolder.getRequestAttributes();
if(ServletRequestAttributes的requestAttributes实例){
HttpServletRequest请求=((ServletRequestAttributes)requestAttributes).getRequest();
if(MockHttpServletRequest的请求实例){
返回(MockHttpServletRequest)请求;
}
}
返回新的MockHttpServletRequest(servletContext);
}
}
注意:它必须在
org.springframework.test.web.servlet.request
包中;否则,它无法扩展所需的
MockHttpServletRequestBuilder

然后,使用
PatchedMockHttpServletRequestBuilder
中的
get()
方法,而不是使用
MockMvcRequestBuilders
中的方法,一切都应该按照您的预期工作

显然,上面的示例重新实现了
get()
,但是您自然可以对
p执行同样的操作
@Autowired protected HttpServletRequest httpRequest;
@Autowired protected HttpServletResponse httpResponse;
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath:spring-config-unit-test.xml")
<bean class="org.springframework.mock.web.MockHttpServletRequest" name="httpRequest" lazy-init="false" />
<bean class="org.springframework.mock.web.MockHttpServletResponse" name="httpResponse" lazy-init="false" />
@RunWith(SpringJUnit4ClassRunner.class)
public class MyTests extends AbstractContextControllerTests {

    @Test
    public void test() {
    }
}
@WebAppConfiguration
@ContextConfiguration(classes = {DispatcherConfig.class}, loader = AnnotationConfigWebContextLoader.class)
public class AbstractContextControllerTests {

    @Autowired
    protected WebApplicationContext wac;

}
@Configuration
@EnableWebMvc
public class DispatcherConfig extends WebMvcConfigurerAdapter {
}