如何与 HtmlUnit 共享 MockMvc 会话?

How can I share a MockMvc session with HtmlUnit?

我正在使用 Spring Test MVC HtmlUnit 和 Geb 来驱动我的 Spring MVC 应用程序的功能测试。我想检查一些会话变量在交互过程中是否正确保存。我尝试为 return 这些变量创建一个测试控制器,但 HtmlUnit 和 mvc.perform() 使用不同的会话。有没有办法在它们之间使用单个共享会话?

驱动程序设置:

MockMvc mvc = MockMvcBuilders.webAppContextSetup(ctx)
    .apply(SecurityMockMvcConfigurers.springSecurity())
    .build()

HtmlUnitDriver driver = MockMvcHtmlUnitDriverBuilder.mockMvcSetup(mvc).javascriptEnabled(true).build()

测试:

when:
    via ProtectedPage

then:
    // this uses session A
    at LoginPage

and:
    // this uses session B
    println mvc.perform(get('/test/sessionAttributes')).andReturn().response.contentAsString

总结

请问您为什么要尝试在 MockMvc 中进行一些工作,而在 HtmlUnit 中进行一些工作?它确实不是为以这种方式使用而设计的。相反,我建议与 HtmlUnit 交互以使用会话(就像您的浏览器一样)并验证这些结果。

这不起作用的原因是 MockMvc.perform 孤立地工作。 HtmlUnit 集成桥接 MockMvc.perform 调用以确保它们按照您在浏览器中的预期工作(即跟踪会话)。但是,桥接 MockMvc.perform 调用的逻辑被封装了。

MockMvc 行为

MockMvc 请求独立工作,默认情况下对每个请求使用一个新会话。例如,以下两个请求在不同的会话上运行:

// this uses session A
mvc.perform(get("/test"))

// this uses session B
mvc.perform(get("/test"))

为了重用会话,您必须从第一次 MockMvc.perform 调用中获取会话并在第二次 MockMvc.perform 调用中设置它。例如:

MvcResult mvcResult = mvc
        .perform(get("/a"))
        .andReturn();

// reuse the previous session   
MockHttpSession session = (MockHttpSession) mvcResult
        .getRequest().getSession();
mvc.perform(get("/b").session(session));

HtmlUnit 支持跟踪 MockMvcWebConnection 中的会话并根据 JSESSIONID cookie 设置适当的会话(类似于您在上面看到的)。

为了在 MockMvc 请求中重用来自 HtmlUnit 支持的 HttpSession,您需要访问原始会话。但是,此逻辑封装在 HtmlUnit 支持中,因此您无法访问它。

解决方法

我不希望我们公开 HtmlUnit 集成的内部结构。我也不建议将 MockMvc HtmlUnit 集成与直接 MockMvc 用法混合和匹配。但是,您可以解决此问题。

第一步是创建一个 ResultHandler 来跟踪上次会话。

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;
    }
}

下一步是确保您在 MockMvc 实例中注册 SessionTracking

SessionTracking sessions = new SessionTracking();

MockMvc mvc = MockMvcBuilders
        .webAppContextSetup(context)
        .apply(SecurityMockMvcConfigurers.springSecurity())
        // ADD THIS
        .alwaysDo(sessions)
        .build();

HtmlUnitDriver driver = MockMvcHtmlUnitDriverBuilder
    .mockMvcSetup(mvc)
    .build();

现在如果您需要发出 MockMvc 请求,您可以使用 SessionTracking 对象访问上一个会话。

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