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:
@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:
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
@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.
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.
This is a timing issue with regard to when Spring's
RequestAttributes
are populated in theRequestContextHolder
. In production I would assume that you are configuring either aRequestContextFilter
or aRequestContextListener
.In any case, manually adding an instance of
RequestContextFilter
to the front of the filter chain in your test will solve the problem.Please note that this will become the default behavior in Spring Framework 4.2: code that simulates
RequestContextFilter
will be implemented directly inMockMvc
. See JIRA issue SPR-13217 for details.As an aside, configuring the
MockHttpServletRequest
that is created by theServletTestExecutionListener
is not supported. If you are usingMockMvc
it is expected that you configure the mocked request via theRequestBuilders
.However, having said that, if you have a concrete need for modifying the mock request created by
ServletTestExecutionListener
manually and then having that re-used withMockMvc
, you can create the following class in your project:Note: it must be in the
org.springframework.test.web.servlet.request
package; otherwise, it can't extendMockHttpServletRequestBuilder
which is required.Then, use the
get()
method fromPatchedMockHttpServletRequestBuilder
instead of fromMockMvcRequestBuilders
, and everything should work as you expect!Obviously, the above example re-implements
get()
, but you can naturally do the same forpost()
, etc.FYI: I might eventually commit the above patched version of
createServletRequest()
to Spring Framework 4.2.x (see JIRA issue SPR-13211).Regards,
Sam (author of the Spring TestContext Framework)