Skip to content

Commit 9c46228

Browse files
committed
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
1 parent 3d95175 commit 9c46228

File tree

2 files changed

+62
-24
lines changed

2 files changed

+62
-24
lines changed

spring-test/src/main/java/org/springframework/test/web/servlet/MockMvc.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@
2626
import org.springframework.mock.web.MockHttpServletRequest;
2727
import org.springframework.mock.web.MockHttpServletResponse;
2828
import org.springframework.util.Assert;
29+
import org.springframework.web.context.request.RequestContextHolder;
30+
import org.springframework.web.context.request.ServletRequestAttributes;
2931

3032
/**
3133
* <strong>Main entry point for server-side Spring MVC test support.</strong>
@@ -48,6 +50,7 @@
4850
*
4951
* @author Rossen Stoyanchev
5052
* @author Rob Winch
53+
* @author Sam Brannen
5154
* @since 3.2
5255
*/
5356
public final class MockMvc {
@@ -140,6 +143,10 @@ public ResultActions perform(RequestBuilder requestBuilder) throws Exception {
140143
final MvcResult mvcResult = new DefaultMvcResult(request, response);
141144
request.setAttribute(MVC_RESULT_ATTRIBUTE, mvcResult);
142145

146+
// [SPR-13217] Simulate RequestContextFilter to ensure that RequestAttributes are
147+
// populated before filters are invoked.
148+
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request, response));
149+
143150
MockFilterChain filterChain = new MockFilterChain(this.servlet, this.filters);
144151
filterChain.doFilter(request, response);
145152

spring-test/src/test/java/org/springframework/test/web/servlet/samples/spr/RequestContextHolderTests.java

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import javax.servlet.ServletException;
2323
import javax.servlet.ServletRequest;
2424
import javax.servlet.ServletResponse;
25-
import javax.servlet.http.HttpServletRequest;
2625

2726
import org.junit.Before;
2827
import org.junit.Test;
@@ -57,7 +56,8 @@
5756
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*;
5857

5958
/**
60-
* Tests for SPR-10025 (access to request attributes via RequestContextHolder)
59+
* Tests for SPR-10025 (access to request attributes via RequestContextHolder),
60+
* SPR-13217 (Populate RequestAttributes before invoking Filters in MockMvc),
6161
* and SPR-13211 (re-use of mock request from the TestContext framework).
6262
*
6363
* @author Rossen Stoyanchev
@@ -72,9 +72,8 @@ public class RequestContextHolderTests {
7272
private static final String FROM_TCF_MOCK = "fromTestContextFrameworkMock";
7373
private static final String FROM_MVC_TEST_DEFAULT = "fromSpringMvcTestDefault";
7474
private static final String FROM_MVC_TEST_MOCK = "fromSpringMvcTestMock";
75-
private static final String FROM_FILTER = "fromFilter";
76-
77-
private static final String ENIGMA = "puzzle";
75+
private static final String FROM_REQUEST_FILTER = "fromRequestFilter";
76+
private static final String FROM_REQUEST_ATTRIBUTES_FILTER = "fromRequestAttributesFilter";
7877

7978
@Autowired
8079
private WebApplicationContext wac;
@@ -91,41 +90,44 @@ public class RequestContextHolderTests {
9190
@Autowired
9291
private SessionScopedService sessionScopedService;
9392

93+
@Autowired
94+
private FilterWithSessionScopedService filterWithSessionScopedService;
95+
9496
private MockMvc mockMvc;
9597

9698

9799
@Before
98100
public void setup() {
99-
this.mockRequest.setAttribute(FROM_TCF_MOCK, ENIGMA);
101+
this.mockRequest.setAttribute(FROM_TCF_MOCK, FROM_TCF_MOCK);
100102

101103
this.mockMvc = webAppContextSetup(this.wac)
102-
.addFilter(new AbcFilter())
103-
.defaultRequest(get("/").requestAttr(FROM_MVC_TEST_DEFAULT, ENIGMA))
104+
.addFilters(new RequestFilter(), new RequestAttributesFilter(), this.filterWithSessionScopedService)
105+
.defaultRequest(get("/").requestAttr(FROM_MVC_TEST_DEFAULT, FROM_MVC_TEST_DEFAULT))
104106
.alwaysExpect(status().isOk())
105107
.build();
106108
}
107109

108110
@Test
109111
public void singletonController() throws Exception {
110-
this.mockMvc.perform(get("/singletonController").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
112+
this.mockMvc.perform(get("/singletonController").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
111113
}
112114

113115
@Test
114116
public void requestScopedController() throws Exception {
115117
assertTrue("request-scoped controller must be a CGLIB proxy", AopUtils.isCglibProxy(this.requestScopedController));
116-
this.mockMvc.perform(get("/requestScopedController").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
118+
this.mockMvc.perform(get("/requestScopedController").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
117119
}
118120

119121
@Test
120122
public void requestScopedService() throws Exception {
121123
assertTrue("request-scoped service must be a CGLIB proxy", AopUtils.isCglibProxy(this.requestScopedService));
122-
this.mockMvc.perform(get("/requestScopedService").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
124+
this.mockMvc.perform(get("/requestScopedService").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
123125
}
124126

125127
@Test
126128
public void sessionScopedService() throws Exception {
127129
assertTrue("session-scoped service must be a CGLIB proxy", AopUtils.isCglibProxy(this.sessionScopedService));
128-
this.mockMvc.perform(get("/sessionScopedService").requestAttr(FROM_MVC_TEST_MOCK, ENIGMA));
130+
this.mockMvc.perform(get("/sessionScopedService").requestAttr(FROM_MVC_TEST_MOCK, FROM_MVC_TEST_MOCK));
129131
}
130132

131133

@@ -167,6 +169,11 @@ public SessionScopedService sessionScopedService() {
167169
public ControllerWithSessionScopedService controllerWithSessionScopedService() {
168170
return new ControllerWithSessionScopedService();
169171
}
172+
173+
@Bean
174+
public FilterWithSessionScopedService filterWithSessionScopedService() {
175+
return new FilterWithSessionScopedService();
176+
}
170177
}
171178

172179
@RestController
@@ -182,7 +189,7 @@ public void handle() {
182189
private static class RequestScopedController {
183190

184191
@Autowired
185-
private HttpServletRequest request;
192+
private ServletRequest request;
186193

187194

188195
@RequestMapping("/requestScopedController")
@@ -195,7 +202,7 @@ public void handle() {
195202
private static class RequestScopedService {
196203

197204
@Autowired
198-
private HttpServletRequest request;
205+
private ServletRequest request;
199206

200207

201208
void process() {
@@ -206,7 +213,7 @@ void process() {
206213
private static class SessionScopedService {
207214

208215
@Autowired
209-
private HttpServletRequest request;
216+
private ServletRequest request;
210217

211218

212219
void process() {
@@ -242,11 +249,35 @@ public void handle() {
242249
}
243250
}
244251

245-
private static class AbcFilter extends GenericFilterBean {
252+
private static class FilterWithSessionScopedService extends GenericFilterBean {
253+
254+
@Autowired
255+
private SessionScopedService service;
256+
257+
258+
@Override
259+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
260+
this.service.process();
261+
assertRequestAttributes(request);
262+
assertRequestAttributes();
263+
chain.doFilter(request, response);
264+
}
265+
}
266+
267+
private static class RequestFilter extends GenericFilterBean {
268+
269+
@Override
270+
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
271+
request.setAttribute(FROM_REQUEST_FILTER, FROM_REQUEST_FILTER);
272+
chain.doFilter(request, response);
273+
}
274+
}
275+
276+
private static class RequestAttributesFilter extends GenericFilterBean {
246277

247278
@Override
248279
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
249-
request.setAttribute(FROM_FILTER, ENIGMA);
280+
RequestContextHolder.getRequestAttributes().setAttribute(FROM_REQUEST_ATTRIBUTES_FILTER, FROM_REQUEST_ATTRIBUTES_FILTER, RequestAttributes.SCOPE_REQUEST);
250281
chain.doFilter(request, response);
251282
}
252283
}
@@ -258,13 +289,13 @@ private static void assertRequestAttributes() {
258289
assertRequestAttributes(((ServletRequestAttributes) requestAttributes).getRequest());
259290
}
260291

261-
private static void assertRequestAttributes(HttpServletRequest request) {
262-
// TODO [SPR-13211] Assert that FOO is ENIGMA, instead of NULL.
263-
// assertThat(this.request.getAttribute(FOO), is(ENIGMA));
292+
private static void assertRequestAttributes(ServletRequest request) {
293+
// TODO [SPR-13211] Assert that FROM_TCF_MOCK is FROM_TCF_MOCK, instead of NULL.
264294
assertThat(request.getAttribute(FROM_TCF_MOCK), is(nullValue()));
265-
assertThat(request.getAttribute(FROM_MVC_TEST_DEFAULT), is(ENIGMA));
266-
assertThat(request.getAttribute(FROM_MVC_TEST_MOCK), is(ENIGMA));
267-
assertThat(request.getAttribute(FROM_FILTER), is(ENIGMA));
295+
assertThat(request.getAttribute(FROM_MVC_TEST_DEFAULT), is(FROM_MVC_TEST_DEFAULT));
296+
assertThat(request.getAttribute(FROM_MVC_TEST_MOCK), is(FROM_MVC_TEST_MOCK));
297+
assertThat(request.getAttribute(FROM_REQUEST_FILTER), is(FROM_REQUEST_FILTER));
298+
assertThat(request.getAttribute(FROM_REQUEST_ATTRIBUTES_FILTER), is(FROM_REQUEST_ATTRIBUTES_FILTER));
268299
}
269300

270301
}

0 commit comments

Comments
 (0)