I am using Spring Test MVC HtmlUnit with Geb to drive functional tests for my Spring MVC application. I would like to check that some session variables are saved properly during an interaction. I tried creating a test controller to return those variables, but HtmlUnit and mvc.perform()
are using different sessions. Is there a way to use a single shared session between them?
driver setup:
MockMvc mvc = MockMvcBuilders.webAppContextSetup(ctx)
.apply(SecurityMockMvcConfigurers.springSecurity())
.build()
HtmlUnitDriver driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(mvc).javascriptEnabled(true).build()
test:
when:
via ProtectedPage
then:
// this uses session A
at LoginPage
and:
// this uses session B
println mvc.perform(get('/test/sessionAttributes')).andReturn().response.contentAsString
Can I ask why you are trying to do some of your work in MockMvc and some of it in HtmlUnit? It really isn't designed to be used this way. Instead, I would recommend interacting with HtmlUnit to consume the session (as your browser would) and verifying those results.
The reason this doesn't work is that MockMvc.perform
works in isolation. The HtmlUnit integration bridges the MockMvc.perform
invocations to ensure they work as you would expect in a browser (i.e. tracking sessions). However, the logic to bridge the the MockMvc.perform
invocations is encapsulated.
MockMvc requests work in isolation and will by default use a new session on every request. For example, the following two requests operate on distinct sessions:
// this uses session A
mvc.perform(get("/test"))
// this uses session B
mvc.perform(get("/test"))
In order to reuse the session, you must obtain the session from the first MockMvc.perform
invocation and set it on the second the MockMvc.perform
invocation. For example:
MvcResult mvcResult = mvc
.perform(get("/a"))
.andReturn();
// reuse the previous session
MockHttpSession session = (MockHttpSession) mvcResult
.getRequest().getSession();
mvc.perform(get("/b").session(session));
The HtmlUnit support keeps track of the sessions in MockMvcWebConnection and sets the appropriate session (similar to what you saw above) based upon the JSESSIONID cookie.
In order for you to reuse the HttpSession
from the HtmlUnit support in a MockMvc
request, you would need access to the original session. However, this logic is encapsulated within the HtmlUnit support and thus you cannot access it.
I don't expect that we will expose the internals of the HtmlUnit integration. I also would not recommend mixing and matching MockMvc
HtmlUnit integration with straight MockMvc
usage. However, you can workaround the issue.
The first step is to create a ResultHandler
that tracks the last session.
public class SessionTracking implements ResultHandler {
private MockHttpSession lastSession;
@Override
public void handle(MvcResult result) throws Exception {
lastSession = (MockHttpSession) result.getRequest().getSession(false);
}
public MockHttpSession getLastSession() {
return lastSession;
}
}
The next step is to ensure you register SessionTracking
with your MockMvc
instance.
SessionTracking sessions = new SessionTracking();
MockMvc mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(SecurityMockMvcConfigurers.springSecurity())
// ADD THIS
.alwaysDo(sessions)
.build();
HtmlUnitDriver driver = MockMvcHtmlUnitDriverBuilder
.mockMvcSetup(mvc)
.build();
Now if you need to make a MockMvc
request, you can access the previous session using the SessionTracking
object.
when:
via ProtectedPage
then:
// this uses session A
at LoginPage
and:
// this uses session A
println mvc.perform(get('/test/sessionAttributes').session(sessions.lastSession).andReturn().response.contentAsString