java Spring-test 集成测试中的自动连线 HttpServletRequest

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30757044/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 17:36:43  来源:igfitidea点击:

Autowired HttpServletRequest in Spring-test integration tests

javaspringspring-mvcspring-testmockmvc

提问by George Lezhava

I am trying to do a test to cover login functionality. Version of Spring is 3.2.12. I have a session bean, declared as:

我正在尝试进行测试以涵盖登录功能。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 works perfectly when run on the server, but when run with the means of spring-test, the problem comes. This is my test method:

这在服务器上运行时效果很好,但是使用spring-test的方式运行时,问题就来了。这是我的测试方法:

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

After debugging, i found out, that when test spring context is started, in the ServletTestExecutionListener.setUpRequestContextIfNecessary an instance of MockHttpServletRequest is created, MockHttpServletRequest request = new MockHttpServletRequest(mockServletContext);? // Lets' call this instance A. And this is the instance that's get injected everywhere, i use

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

@Autowired
HttpServletRequest request;

Whereas, calling MockMvc.perform, creates another instance of MockHttpServletRequest (Let's call it instance B), which is passed to all the filters, servlets, etc. So, basically, the attribute i set in the filter in the request, can't be read in the ClientSessionServiceImpl, because different instance of MockHttpServletRequest is injected there.

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

I spent bunch of time on this, but still have not found the solution.

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

P.S. I searched through StackOverflow, there are questions with similar titles, but describing the problems that differ from mine, as i don't want to pass HttpServletRequest as a parameter, and would prefer to have it Autowired, unless there is a good reason for it.

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

采纳答案by Sam Brannen

So, basically, the attribute i set in the filter in the request, can't be read in the ClientSessionServiceImpl, because different instance of MockHttpServletRequest is injected there.

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

This is a timing issue with regard to when Spring's RequestAttributesare populated in the RequestContextHolder. In production I would assume that you are configuring either a RequestContextFilteror a RequestContextListener.

这是关于 SpringRequestAttributesRequestContextHolder. 在生产中,我假设您正在配置 aRequestContextFilterRequestContextListener.

In any case, manually adding an instance of RequestContextFilterto the front of the filter chain in your test will solve the problem.

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

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

Please note that this will become the default behavior in Spring Framework 4.2: code that simulates RequestContextFilterwill be implemented directly in MockMvc. See JIRA issue SPR-13217for details.

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



As an aside, configuring the MockHttpServletRequestthat is created by the ServletTestExecutionListeneris not supported. If you are using MockMvcit is expected that you configure the mocked request via the RequestBuilders.

顺便说一句,不支持配置由MockHttpServletRequest创建的ServletTestExecutionListener。如果您正在使用MockMvc,则需要通过RequestBuilders.

However, having said that, if you have a concrete need for modifying the mock request created by ServletTestExecutionListenermanually and then having that re-used with MockMvc, you can create the following class in your project:

但是,话虽如此,如果您确实需要修改ServletTestExecutionListener手动创建的模拟请求,然后重新使用 with 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);
    }

}

Note: it mustbe in the org.springframework.test.web.servlet.requestpackage; otherwise, it can't extend MockHttpServletRequestBuilderwhich is required.

注意:必须org.springframework.test.web.servlet.request包装内;否则,它不能扩展MockHttpServletRequestBuilder这是必需的。

Then, use the get()method from PatchedMockHttpServletRequestBuilderinstead of from MockMvcRequestBuilders, and everything should work as you expect!

然后,使用get()fromPatchedMockHttpServletRequestBuilder而不是 from的方法MockMvcRequestBuilders,一切都应该如你所愿!

Obviously, the above example re-implementsget(), but you can naturally do the same for post(), etc.

显然,上面的示例重新实现了get(),但您自然可以对post()等执行相同的操作。

FYI: I might eventually commit the above patched version of createServletRequest()to Spring Framework 4.2.x (see JIRA issue SPR-13211).

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

Regards,

问候,

Sam (author of the Spring TestContext Framework)

Sam(Spring TestContext Framework 的作者

回答by csf

i had a similar issue where i had both HttpServletRequest and HttpServletResponse autowired in my rest controller

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

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

However when I try to use spring test using below configuration , test failed due to it unable to autowire httpRequestdue to test scope .

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

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

Solution after searching web i declared the mock request and response as fist line of my bean definition(unit test) xml like below . hope this help some one

搜索网络后的解决方案我将模拟请求和响应声明为我的 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" />

回答by zygimantus

Try this configuration:

试试这个配置:

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

    @Test
    public void test() {
    }
}

Where AbstractContextControllerTestsis this:

其中AbstractContextControllerTests是这样的:

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

    @Autowired
    protected WebApplicationContext wac;

}

And DispatherConfigis this:

DispatherConfig是这样的:

@Configuration
@EnableWebMvc
public class DispatcherConfig extends WebMvcConfigurerAdapter {
}

回答by Master Slave

You can use RequestPostProcessorto swap out the request created inside perform. You can add a util method to your test, something like

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

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

you would apply this post processor by adding it via withmethod

你可以通过with方法添加它来应用这个后处理器

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

here the requestyou pass to mockedRequestmethod, would be your original request holding the attributes you need

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