介绍 Spring Test MVC HtmlUnit

工程 | Rob Winch | 2014年3月19日 | ...

星期一,我宣布发布了Spring Test MVC HtmlUnit的首个里程碑版本,并承诺将撰写一系列博客文章来介绍它。这是介绍 Spring Test MVC HtmlUnit 的四部分博客系列中的第一篇。系列大纲如下所示

为什么选择 Spring Test MockMvc HtmlUnit?

最显而易见的问题是“我为什么需要这个?”答案最好通过探索一个非常基本的示例应用程序来找到。假设您有一个 Spring MVC Web 应用程序,允许对Message对象执行 CRUD 操作。该应用程序还允许分页浏览所有消息。您将如何测试它?

使用 Spring MVC Test,我们可以轻松测试我们是否能够创建一个Message

MockHttpServletRequestBuilder createMessage = post("/messages/")
	.param("summary", "Spring Rocks")
	.param("text", "In case you didn't know, Spring Rocks!");

mockMvc.perform(createMessage)
	.andExpect(status().is3xxRedirection())
	.andExpect(redirectedUrl("/messages/123"));

如果我们想测试允许我们创建消息的表单视图呢?例如,假设我们的表单如下所示

<form id="messageForm" action="/messages/" method="post">
  <div class="pull-right"><a href="/messages/">Messages</a></div>

  <label for="summary">Summary</label>
  <input type="text" class="required" id="summary" name="summary" value="" />

  <label for="text">Message</label>
  <textarea id="text" name="text"></textarea>

  <div class="form-actions">
    <input type="submit" value="Create" />
  </div>
</form>

我们如何确保我们的表单将生成正确的请求以创建新消息?一个简单的尝试看起来像这样

mockMvc.perform(get("/messages/form"))
	.andExpect(xpath("//input[@name='summary']").exists())
	.andExpect(xpath("//textarea[@name='text']").exists());

此测试有一些明显的问题。如果我们将控制器更新为使用参数“message”而不是“text”,我们的测试将错误地通过。为了解决这个问题,我们可以结合我们的两个测试

String summaryParamName = "summary";
String textParamName = "text";
mockMvc.perform(get("/messages/form"))
	.andExpect(xpath("//input[@name='" + summaryParamName + "']").exists())
	.andExpect(xpath("//textarea[@name='" + textParamName + "']").exists());

MockHttpServletRequestBuilder createMessage = post("/messages/")
	.param(summaryParamName, "Spring Rocks")
	.param(textParamName, "In case you didn't know, Spring Rocks!");

mockMvc.perform(createMessage)
	.andExpect(status().is3xxRedirection())
	.andExpect(redirectedUrl("/messages/123"));

这将降低我们的测试错误地通过的风险,但仍然存在一些问题

  • 如果我们的页面上有多个表单怎么办?诚然,我们可以更新我们的 xpath 表达式,但是当我们考虑更多因素时(字段类型是否正确,字段是否启用等),它们会变得更加复杂。
  • 另一个问题是我们在做我们预期工作量的两倍。我们必须首先验证视图,然后使用我们刚刚验证的相同参数提交视图。理想情况下,这可以一次完成。
  • 最后,还有一些我们仍然无法解释的事情。例如,如果表单具有我们也希望验证的 JavaScript 验证怎么办?

总体问题是测试网页不是单一交互。相反,它是用户如何与网页交互以及该网页如何与其他资源交互的组合。例如,表单视图的结果用作用户创建消息的输入。另一个例子是我们的表单视图利用了额外的资源,例如 JavaScript 验证,这些资源会影响页面的行为。

集成测试来拯救?

为了解决上述问题,我们可以执行集成测试,但这有一些明显的缺点。考虑测试允许我们分页浏览消息的视图。我们可能需要以下测试

  • 当消息为空时,我们的页面是否向用户显示一条消息,指示没有可用结果?
  • 我们的页面是否正确显示单个消息?
  • 我们的页面是否正确支持分页?

为了设置这些测试,我们需要确保我们的数据库中包含正确的消息。这导致了许多问题

  • 确保数据库中存在正确的消息可能很繁琐(考虑可能的外部键)。
  • 测试速度会很慢,因为每个测试都需要确保数据库处于正确的状态。
  • 由于我们的数据库需要处于特定状态,因此我们无法并行运行测试。
  • 对自动生成的 ID、时间戳等内容的断言可能具有挑战性。

这些问题并不意味着我们应该完全放弃集成测试。相反,我们可以通过将详细测试转移到使用模拟服务(速度会快得多)来减少集成测试的数量。然后,我们可以使用更少的集成测试来验证简单的流程,以确保一切都能正常协同工作。

进入 Spring Test MVC HtmlUnit

那么,我们如何在测试页面交互的同时获得性能呢?我相信您已经猜到了……Spring Test MVC HtmlUnit 将允许我们

  • 使用我们已经在集成测试中使用的工具(即 HtmlUnit、WebDriver 和 Geb)轻松测试我们的页面,无需启动应用程序服务器
  • 支持 JavaScript 测试
  • 可以选择使用模拟服务来加快测试速度。

注意:与 Spring MVC Test 一样,HtmlUnit 集成将与不依赖于 Servlet 容器的模板技术(即 Thymeleaf、Freemarker、Velocity 等)一起工作。它不适用于 JSP,因为它们依赖于 Servlet 容器。

接下来……

我希望这篇帖子能让你对我的下一篇帖子感到兴奋,该帖子将讨论如何通过集成 Spring Test MVC 和 HtmlUnit 来解决其中一些问题。


请提供反馈!

如果您对本博客系列或 Spring Test MVC HtmlUnit 有任何反馈,欢迎您通过github issues与我们联系,或在 Twitter 上与我联系@rob_winch。当然,最好的反馈形式是贡献

获取 Spring 电子邮件简报

通过 Spring 电子邮件简报保持联系

订阅

领先一步

VMware 提供培训和认证,以加快您的进步。

了解更多

获取支持

Tanzu Spring在一个简单的订阅中提供对 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部