Spring Framework 3.2 RC1:Spring MVC 测试框架

工程 | Rossen Stoyanchev | 2012年11月12日 | ...

[callout title=更新 2012年12月19日] 最终版 Spring Framework 参考文档包含迁移指南以及关于 Spring MVC Test 的完整章节。[/callout]

上周 Juergen Hoeller 宣布发布 Spring Framework 3.2 RC1,而 Sam Brannen 讨论了其 spring-test 模块中令人兴奋的新增功能,例如对 WebApplicationContext 的支持以及加载上下文层次结构的未来计划。今天我将继续这个话题,介绍另一个令人兴奋的 spring-test 新增功能。在 3.2 RC1 中,我们为测试 Spring MVC 应用添加了一流的支持,包括客户端和服务器端。

背景

这里讨论的 Spring MVC Test 框架源自 Github 上的一个独立项目,其功能经过一年多的时间,在众多用户的持续反馈下不断演进。感谢所有早期使用者、所有贡献者报告问题的人、发表评论的人以及所有撰写博客或提及此项目的人。

截至 Spring 3.2 RC1,独立项目中的代码已添加到 Spring Framework 中,并在 spring-test 模块中提供,包名略有修改,并支持 3.2 的特定功能,如异步请求等。独立项目将继续存在,用于测试 Spring MVC 3.1 的应用。

闲话少说,让我们更仔细、更详细地看看。

服务器端支持

今天你如何测试 Spring MVC 控制器?最有可能通过简单的单元测试,可能涉及到 MockHttpServletRequest-Response。做起来相当简单,但这测试得不够充分。控制器有注解,用于表达它们如何映射、需要提取、转换和验证哪些请求数据、是否写入响应体、如何处理异常等等。如果你只编写简单的单元测试,框架因这些注解而执行的所有功能都未得到测试。

如果你可以重写这些控制器单元测试,但不是直接调用控制器,而是通过 DispatcherServlet 来完成,就像运行时一样,会怎样?如果你可以使用流畅的 API 来指定要执行的请求和期望的响应,又会怎样?所有这些都不需要 servlet 容器。这就是 Spring MVC Test 所做的事情。这里有一个例子


@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("servlet-context.xml")
public class SampleTests {

  @Autowired
  private WebApplicationContext wac;

  private MockMvc mockMvc;

  @Before
  public void setup() {
    this.mockMvc = webAppContextSetup(this.wac).build();
  }

  @Test
  public void getFoo() throws Exception {
    this.mockMvc.perform(get("/foo").accept("application/json"))
        .andExpect(status().isOk())
        .andExpect(content().mimeType("application/json"))
        .andExpect(jsonPath("$.name").value("Lee"));
  }
}

[callout title=静态导入]流畅的 API 依赖于这些静态导入:MockMvcBuilders.* MockMvcRequestBuilders.* MockMvcResultMatchers.* 为了代码补全的便利,请将它们添加到 Eclipse 首选项的“favorite types”中,或者只需记住以 MockMvc* 开头的类。[/callout]

如你所见,我们使用新的 @WebAppConfiguration 注解来加载我们的 Spring MVC 配置。然后我们将生成的 WebApplicationContext 注入到测试类的字段中,并用它创建一个 MockMvc,后者反过来用于执行请求和定义期望。

[callout title=上下文缓存]TestContext 框架会在整个测试套件甚至整个 JVM 中缓存已加载的 Spring 配置。因此,测试的速度应该非常理想。[/callout]

正如现有的控制器单元测试一样,Spring MVC Test 基于 spring-test 中的 mock 请求和响应构建,并且不需要运行中的 servlet 容器。主要区别在于实际的 Spring MVC 配置是通过 TestContext 框架加载的,并且请求是通过实际调用 DispatcherServlet 和所有与运行时相同的 Spring MVC 基础设施来执行的。

与现有的控制器单元测试类似,你也可以考虑向控制器注入 mock 服务,以便专注于测试 Web 层,并避免例如访问数据库。因此,你可以加载创建 mock 对象的配置,而不是加载实际的业务和持久化服务。例如


@Configuration
public class MyConfig {

    @Bean
    public FooService fooService() {
        return Mockito.mock(FooService.class);
    }

}

或者在 XML 配置中


<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="org.example.FooService"/>
</bean>

考虑到我们没有运行在实际的 servlet 容器中,哪些功能可以工作,哪些不能呢?大部分功能将像运行时一样工作。你甚至可以注册 Servlet 过滤器,从而支持像 Spring Security 这样的功能。大多数渲染技术,如 JSON/XML、Freemarker、Velocity、Thymeleaf、Excel、PDF 等,都会工作。唯一被排除的渲染技术是 JSP,因为它需要 servlet 容器。对于 JSP,你仍然可以验证请求被转发到哪个 JSP,模型中有哪些属性,是否抛出了任何异常等等。

客户端 REST 测试

客户端 REST 测试背后的理念是什么?如果你的代码使用了 RestTemplate,你可能想对其进行测试,你可以针对正在运行的服务器进行测试,或者 mock RestTemplate。客户端 REST 测试支持提供了第三种选择,即使用实际的 RestTemplate,但通过自定义的 ClientHttpRequestFactory 进行配置,该工厂会对照实际请求检查期望,并返回 stub 响应。


RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);

mockServer.expect(requestTo("/greeting"))
  .andRespond(withSuccess("Hello world", "text/plain"));

// use RestTemplate ...

mockServer.verify();

[callout title=静态导入]流畅的 API 需要这些静态导入
MockRestRequestMatchers.* MockRestResponseCreators.* 为了代码补全的便利,请将它们添加到 Eclipse 首选项的“favorite types”中,或者只需记住以 "MockRest*" 开头的类。[/callout]

如你所见,我们创建一个 RestTemplate 实例并将其传递给 MockRestServiceServer 进行配置。然后我们定义期望请求的特征,并提供要返回的 stub 响应。我们可以定义任意数量的期望请求和 stub 响应。在测试结束时,我们可以使用 verify 方法检查所有期望的请求是否都已执行。

后续步骤

还有很多可以讨论的内容,但最好还是在你自己的项目中使用 Spring Framework 3.2 RC1 或 Github 上的独立项目(适用于 Spring Framework 3.1)进行尝试。

最近我亲自尝试了一下,为 spring-mvc-showcase 的所有控制器方法添加了一套全面的测试。这是一次有益的实践,因为它帮我发现了一个 bug。因此,我希望它也能对其他人有所帮助。

如果你需要更多示例,Spring Framework 中还有许多演示测试,包括异步请求测试过滤器测试JSON 响应XML 响应,以及许多其他示例。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获取支持

Tanzu Spring 通过一项简单订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将到来的活动

查看 Spring 社区所有即将到来的活动。

查看全部