Populate RequestAttributes before invoking Filters in MockMvc
When using the Spring TestContext Framework (TCF) to load a WebApplicationContext and the Spring MVC Test Framework (MockMvc) to test a controller, two instances of MockHttpServletRequest will be created. Due to an ordering issue with regard to population of the RequestAttributes, it is therefore possible that a filter accesses the mocked request managed by the TCF, while the controller accesses the mocked request managed by MockMvc, and this leads to test failures if the controller expects data from the filter to be present in the request. This commit fixes this bug by ensuring that the RequestAttributes backed by the mocked request managed by MockMvc are stored in the RequestContextHolder before any filters are invoked by MockMvc. Issue: SPR-13217
This commit is contained in:
@@ -22,7 +22,6 @@ import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -57,7 +56,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
|
||||
|
||||
/**
|
||||
* Tests for SPR-10025 (access to request attributes via RequestContextHolder)
|
||||
* Tests for SPR-10025 (access to request attributes via RequestContextHolder),
|
||||
* SPR-13217 (Populate RequestAttributes before invoking Filters in MockMvc),
|
||||
* and SPR-13211 (re-use of mock request from the TestContext framework).
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -72,9 +72,8 @@ public class RequestContextHolderTests {
|
||||
private static final String FROM_TCF_MOCK = "fromTestContextFrameworkMock";
|
||||
private static final String FROM_MVC_TEST_DEFAULT = "fromSpringMvcTestDefault";
|
||||
private static final String FROM_MVC_TEST_MOCK = "fromSpringMvcTestMock";
|
||||
private static final String FROM_FILTER = "fromFilter";
|
||||
|
||||
private static final String ENIGMA = "puzzle";
|
||||
private static final String FROM_REQUEST_FILTER = "fromRequestFilter";
|
||||
private static final String FROM_REQUEST_ATTRIBUTES_FILTER = "fromRequestAttributesFilter";
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext wac;
|
||||
@@ -91,41 +90,44 @@ public class RequestContextHolderTests {
|
||||
@Autowired
|
||||
private SessionScopedService sessionScopedService;
|
||||
|
||||
@Autowired
|
||||
private FilterWithSessionScopedService filterWithSessionScopedService;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.mockRequest.setAttribute(FROM_TCF_MOCK, ENIGMA);
|
||||
this.mockRequest.setAttribute(FROM_TCF_MOCK, FROM_TCF_MOCK);
|
||||
|
||||
this.mockMvc = webAppContextSetup(this.wac)
|
||||
.addFilter(new AbcFilter())
|
||||
.defaultRequest(get("/").requestAttr(FROM_MVC_TEST_DEFAULT, ENIGMA))
|
||||
.addFilters(new RequestFilter(), new RequestAttributesFilter(), this.filterWithSessionScopedService)
|
||||
.defaultRequest(get("/").requestAttr(FROM_MVC_TEST_DEFAULT, FROM_MVC_TEST_DEFAULT))
|
||||
.alwaysExpect(status().isOk())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singletonController() throws Exception {
|
||||
this.mockMvc.perform(get("/singletonController").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
|
||||
this.mockMvc.perform(get("/singletonController").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestScopedController() throws Exception {
|
||||
assertTrue("request-scoped controller must be a CGLIB proxy", AopUtils.isCglibProxy(this.requestScopedController));
|
||||
this.mockMvc.perform(get("/requestScopedController").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
|
||||
this.mockMvc.perform(get("/requestScopedController").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestScopedService() throws Exception {
|
||||
assertTrue("request-scoped service must be a CGLIB proxy", AopUtils.isCglibProxy(this.requestScopedService));
|
||||
this.mockMvc.perform(get("/requestScopedService").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
|
||||
this.mockMvc.perform(get("/requestScopedService").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sessionScopedService() throws Exception {
|
||||
assertTrue("session-scoped service must be a CGLIB proxy", AopUtils.isCglibProxy(this.sessionScopedService));
|
||||
this.mockMvc.perform(get("/sessionScopedService").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
|
||||
this.mockMvc.perform(get("/sessionScopedService").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +169,11 @@ public class RequestContextHolderTests {
|
||||
public ControllerWithSessionScopedService controllerWithSessionScopedService() {
|
||||
return new ControllerWithSessionScopedService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterWithSessionScopedService filterWithSessionScopedService() {
|
||||
return new FilterWithSessionScopedService();
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
@@ -182,7 +189,7 @@ public class RequestContextHolderTests {
|
||||
private static class RequestScopedController {
|
||||
|
||||
@Autowired
|
||||
private HttpServletRequest request;
|
||||
private ServletRequest request;
|
||||
|
||||
|
||||
@RequestMapping("/requestScopedController")
|
||||
@@ -195,7 +202,7 @@ public class RequestContextHolderTests {
|
||||
private static class RequestScopedService {
|
||||
|
||||
@Autowired
|
||||
private HttpServletRequest request;
|
||||
private ServletRequest request;
|
||||
|
||||
|
||||
void process() {
|
||||
@@ -206,7 +213,7 @@ public class RequestContextHolderTests {
|
||||
private static class SessionScopedService {
|
||||
|
||||
@Autowired
|
||||
private HttpServletRequest request;
|
||||
private ServletRequest request;
|
||||
|
||||
|
||||
void process() {
|
||||
@@ -242,11 +249,35 @@ public class RequestContextHolderTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static class AbcFilter extends GenericFilterBean {
|
||||
private static class FilterWithSessionScopedService extends GenericFilterBean {
|
||||
|
||||
@Autowired
|
||||
private SessionScopedService service;
|
||||
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
request.setAttribute(FROM_FILTER, ENIGMA);
|
||||
this.service.process();
|
||||
assertRequestAttributes(request);
|
||||
assertRequestAttributes();
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RequestFilter extends GenericFilterBean {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
request.setAttribute(FROM_REQUEST_FILTER, FROM_REQUEST_FILTER);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RequestAttributesFilter extends GenericFilterBean {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
RequestContextHolder.getRequestAttributes().setAttribute(FROM_REQUEST_ATTRIBUTES_FILTER, FROM_REQUEST_ATTRIBUTES_FILTER, RequestAttributes.SCOPE_REQUEST);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -258,13 +289,13 @@ public class RequestContextHolderTests {
|
||||
assertRequestAttributes(((ServletRequestAttributes) requestAttributes).getRequest());
|
||||
}
|
||||
|
||||
private static void assertRequestAttributes(HttpServletRequest request) {
|
||||
// TODO [SPR-13211] Assert that FOO is ENIGMA, instead of NULL.
|
||||
// assertThat(this.request.getAttribute(FOO), is(ENIGMA));
|
||||
private static void assertRequestAttributes(ServletRequest request) {
|
||||
// TODO [SPR-13211] Assert that FROM_TCF_MOCK is FROM_TCF_MOCK, instead of NULL.
|
||||
assertThat(request.getAttribute(FROM_TCF_MOCK), is(nullValue()));
|
||||
assertThat(request.getAttribute(FROM_MVC_TEST_DEFAULT), is(ENIGMA));
|
||||
assertThat(request.getAttribute(FROM_MVC_TEST_MOCK), is(ENIGMA));
|
||||
assertThat(request.getAttribute(FROM_FILTER), is(ENIGMA));
|
||||
assertThat(request.getAttribute(FROM_MVC_TEST_DEFAULT), is(FROM_MVC_TEST_DEFAULT));
|
||||
assertThat(request.getAttribute(FROM_MVC_TEST_MOCK), is(FROM_MVC_TEST_MOCK));
|
||||
assertThat(request.getAttribute(FROM_REQUEST_FILTER), is(FROM_REQUEST_FILTER));
|
||||
assertThat(request.getAttribute(FROM_REQUEST_ATTRIBUTES_FILTER), is(FROM_REQUEST_ATTRIBUTES_FILTER));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user