繁体   English   中英

在Spring-test集成测试中自动连接HttpServletRequest

[英]Autowired HttpServletRequest in Spring-test integration tests

我正在尝试进行测试以涵盖登录功能。 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
    }

这在服务器上运行时效果很好,但是当使用弹簧测试运行时,问题就出现了。 这是我的测试方法:

this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(springSecurityFilterChain).build();
mockMvc.perform(post(URL));

经过调试,我发现,当测试spring上下文启动时,在ServletTestExecutionListener.setUpRequestContextIfNecessary中创建一个MockHttpServletRequest实例,MockHttpServletRequest request = new MockHttpServletRequest(mockServletContext); //让我们调用这个实例A.这是我用的所有注入的实例

@Autowired
HttpServletRequest request;

然而,调用MockMvc.perform,创建另一个MockHttpServletRequest实例(让我们称之为实例B),它被传递给所有过滤器,servlet等。所以,基本上,我在请求中设置过滤器的属性,不能在ClientSessionServiceImpl中读取,因为在那里注入了不同的MockHttpServletRequest实例。

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

PS我通过StackOverflow搜索,有类似标题的问题,但描述了与我的不同的问题,因为我不想传递HttpServletRequest作为参数,并且宁愿让它自动装配,除非有充分的理由它。

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

这是关于Spring的RequestAttributes何时填充在RequestContextHolder的时间问题。 在生产中,我假设您正在配置RequestContextFilterRequestContextListener

无论如何,在测试中手动将RequestContextFilter实例添加到过滤器链的前面将解决问题。

mockMvc = MockMvcBuilders
  .webAppContextSetup(this.wac)
  .addFilters(new RequestContextFilter(), testFilterChain)
  .build();

请注意,这将成为Spring框架4.2的默认行为:代码模拟RequestContextFilter将直接在实施MockMvc 有关详细信息,请参阅JIRA问题SPR-13217


另外,不支持配置由ServletTestExecutionListener创建的MockHttpServletRequest 如果您使用的是MockMvc ,则需要通过RequestBuilders配置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);
    }

}

注意:它必须位于org.springframework.test.web.servlet.request包中; 否则,它无法扩展所需的MockHttpServletRequestBuilder

然后,使用PatchedMockHttpServletRequestBuilderget()方法而不是MockMvcRequestBuilders ,并且一切都应该按预期工作!

显然,上面的例子重新实现了 get() ,但你可以自然地为post()等做同样的事情。

仅供参考:我最终可能会将上述修补版本的createServletRequest()提交给Spring Framework 4.2.x(参见JIRA问题SPR-13211 )。

问候,

Sam( Spring TestContext Framework的作者

我有一个类似的问题,我在我的休息控制器中自动装配了HttpServletRequest和HttpServletResponse

@Autowired protected HttpServletRequest httpRequest;
@Autowired protected HttpServletResponse httpResponse;

但是,当我尝试使用下面的配置使用spring测试时,由于测试范围无法自动装配httpRequest ,测试失败。

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("classpath:spring-config-unit-test.xml")

搜索web后的解决方案我将模拟请求和响应声明为我的bean定义(单元测试)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" />

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

private static RequestPostProcessor mockedRequest(final MockHttpServletRequest mockHttpServletRequest) {
    return new RequestPostProcessor() {
        @Override
        public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
            return mockHttpServletRequest;
        }
    };
}

你会被通过将其应用于该后处理器with方法

    this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(springSecurityFilterChain).build();
    mockMvc.perform(post(URL).with(mockedRequest(request)));

这里你传递给mockedRequest方法的request将是你原始的请求,包含你需要的属性

试试这个配置:

@RunWith(SpringJUnit4ClassRunner.class)
public class MyTests extends AbstractContextControllerTests {

    @Test
    public void test() {
    }
}

AbstractContextControllerTests的位置是这样的:

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

    @Autowired
    protected WebApplicationContext wac;

}

DispatherConfig是这样的:

@Configuration
@EnableWebMvc
public class DispatcherConfig extends WebMvcConfigurerAdapter {
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM