Skip to content

Reuse MockHttpServletRequest from ServletTestExecutionListener in Spring MVC Test framework [SPR-13211] #17803

@spring-projects-issues

Description

@spring-projects-issues

Sam Brannen opened SPR-13211 and commented

Status Quo

As discussed in the referenced question on Stack Overflow, the combination of loading a WebApplicationContext with the Spring TestContext Framework and using the Spring MVC Test Framework results in two instances of MockHttpServletRequest being created.

  1. ServletTestExecutionListener.setUpRequestContextIfNecessary() creates a MockHttpServletRequest and makes it available to the WebApplicationContext via the RequestAttributes stored as a thread-local variable in the RequestContextHolder.
    • The mocked request is then injected into any session-scoped or request-scoped bean in the WebApplicationContext (following standard Spring semantics for scoped beans) or into the test instance.
  2. MockMvc.perform() creates another MockHttpServletRequest (via MockHttpServletRequestBuilder.createServletRequest()).
    • This mocked request is the one that is eventually supplied to the TestDispatcherServlet in MockMvc.perform() (via the MockFilterChain).

The end result is that there are two mocked requests. Consequently, components that have the request injected into them (following scoped-proxy semantics) will interact with a request object that has nothing to do with the request configured via RequestBuilders (e.g., get(), post(), etc.) in Spring MVC Test.

NB: the above assumptions regarding interactions between mocked requests and session/request-scoped beans have not been fully confirmed, but in any case it is possible to have a MockHttpServletRequest injected into a test that is then not used by Spring MVC Test, which leads to confusing results. For example, request parameters configured in the mock request created by the ServletTestExecutionListener are not visible to controller methods invoked via Spring MVC Test.


Deliverables

TBD


Proposal

Initial analysis shows that modifying the createServletRequest() method in MockHttpServletRequestBuilder as follows could work. In fact, no side effects have been uncovered within Spring's test suite as a result of this change.

/**
 * 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
 */
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);
}

However, special care should be taken with regard to MockMultipartHttpServletRequestBuilder.createServletRequest() since it creates a MockMultipartHttpServletRequest instead of a MockHttpServletRequest. Furthermore, potential side effects for third-party add-ons (e.g., the upcoming support for HtmlUnit in Spring MVC Test (#17749)) must be taken into consideration as well.


Affects: 3.2 GA

Reference URL: http://stackoverflow.com/questions/30757044/autowired-httpservletrequest-in-spring-test-integration-tests

Issue Links:

Referenced from: commits 5e24ee9, 3c799e6, bf06bf3, 3c2efee

0 votes, 5 watchers

Metadata

Metadata

Assignees

Labels

in: testIssues in the test modulestatus: invalidAn issue that we don't feel is valid

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions