java - Autowired HttpServletRequest in Spring-test integration tests -
i trying test cover login functionality. version of spring 3.2.12. have session bean, declared as:
@service @scope(value = "session", proxymode = scopedproxymode.interfaces) public class clientsessionserviceimpl implements clientsessionservice { @autowired private httpservletrequest request; // method called during login routine filter public boolean checkuser() { // rely on request attributes here, set in filter }
this works when run on server, when run means of spring-test, problem comes. test method:
this.mockmvc = mockmvcbuilders.webappcontextsetup(this.wac).addfilter(springsecurityfilterchain).build(); mockmvc.perform(post(url));
after debugging, found out, when test spring context started, in servlettestexecutionlistener.setuprequestcontextifnecessary instance of mockhttpservletrequest created, mockhttpservletrequest request = new mockhttpservletrequest(mockservletcontext); // lets' call instance a. , instance that's injected everywhere, use
@autowired httpservletrequest request;
whereas, calling mockmvc.perform, creates instance of mockhttpservletrequest (let's call instance b), passed filters, servlets, etc. so, basically, attribute set in filter in request, can't read in clientsessionserviceimpl, because different instance of mockhttpservletrequest injected there.
i spent bunch of time on this, still have not found solution.
p.s. searched through stackoverflow, there questions similar titles, describing problems differ mine, don't want pass httpservletrequest parameter, , prefer have autowired, unless there reason it.
so, basically, attribute set in filter in request, can't read in clientsessionserviceimpl, because different instance of mockhttpservletrequest injected there.
this timing issue regard when spring's requestattributes
populated in requestcontextholder
. in production assume configuring either requestcontextfilter
or requestcontextlistener
.
in case, manually adding instance of requestcontextfilter
front of filter chain in test solve problem.
mockmvc = mockmvcbuilders .webappcontextsetup(this.wac) .addfilters(new requestcontextfilter(), testfilterchain) .build();
please note become default behavior in spring framework 4.2: code simulates requestcontextfilter
implemented directly in mockmvc
. see jira issue spr-13217 details.
as aside, configuring mockhttpservletrequest
created servlettestexecutionlistener
not supported. if using mockmvc
expected configure mocked request via requestbuilders
.
however, having said that, if have concrete need modifying mock request created servlettestexecutionlistener
manually , having re-used mockmvc
, can create following class in project:
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 {@link mockhttpservletrequest}. * <p>if instance of {@code mockhttpservletrequest} available via * {@link requestattributes} bound current thread in * {@link requestcontextholder}, method returns instance. * <p>otherwise, method creates new {@code mockhttpservletrequest} * based on supplied {@link servletcontext}. * <p>can 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: must in org.springframework.test.web.servlet.request
package; otherwise, can't extend mockhttpservletrequestbuilder
required.
then, use get()
method patchedmockhttpservletrequestbuilder
instead of mockmvcrequestbuilders
, , should work expect!
obviously, above example re-implements get()
, can naturally same post()
, etc.
fyi: might commit above patched version of createservletrequest()
spring framework 4.2.x (see jira issue spr-13211).
regards,
sam (author of spring testcontext framework)
Comments
Post a Comment