领先一步
VMware 提供培训和认证,助您快速提升技能。
了解更多在我的第二篇文章中,我描述了如何使用 Spring MVC Test 和 HtmlUnit。在这篇文章中,我们将利用WebDriver中额外的抽象来使事情变得更加容易。
我们已经可以使用 HtmlUnit 和 MockMvc 了,那么为什么我们还要使用 WebDriver 呢?WebDriver 提供了一个非常优雅的 API,并且允许我们轻松地组织代码。为了更好地理解,让我们探索一个示例。
注意 尽管是 Selenium 的一部分,但 WebDriver 不需要 Selenium Server 来运行您的测试。
假设我们需要确保正确创建了一条消息。测试涉及查找 html 输入,填写它们,并进行各种断言。
有很多测试,因为我们也想要测试错误条件。例如,我们希望确保如果我们只填写了表单的一部分,我们会得到一个错误。如果我们填写了整个表单,则新创建的消息随后会显示。
如果其中一个字段名为“summary”,那么我们可能会在测试中的所有地方重复以下内容
HtmlTextInput summaryInput = createMsgFormPage.getHtmlElementById("summary");
summaryInput.setValueAttribute("Spring Rocks");
那么如果我们将 id 更改为“smmry”会发生什么?这意味着我们将不得不更新所有测试!相反,我们希望我们编写了更优雅的代码,其中填写表单在它自己的方法中
public HtmlPage createMessage(HtmlPage currentPage, String summary, String text) {
...
setSummary(currentPage, summary);
...
}
public void setSummary(HtmlPage currentPage, String summary) {
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
}
这确保了如果我们更改 UI,我们不必更新所有测试。
我们可以更进一步,将此逻辑放在表示我们当前所在的HtmlPage
的对象中。
public class CreateMessagePage {
private final HtmlPage currentPage;
...
public T createMessage(Class<T> resultPage, String summary, String text) {
...
setSummary(currentPage, summary);
...
HtmlPage result = submit.click();
...
return (T) error ? new CreateMessagePage(result) : new ViewMessagePage(result);
}
public void setSummary(String summary) {
HtmlTextInput summaryInput = currentPage.getHtmlElementById("summary");
summaryInput.setValueAttribute(summary);
}
}
以前,这种模式被称为页面对象模式。虽然我们当然可以使用 HtmlUnit 来做到这一点,但 WebDriver 提供了一些工具,我们将在以下部分中探讨这些工具,使这种模式变得更容易。
在使用该项目之前,您必须确保更新您的依赖项。有关 Maven 和 Gradle 的说明可以在站点文档中找到。
现在我们有了正确的依赖项,我们可以在单元测试中使用 WebDriver。我们的示例假设您已经将 JUnit 作为依赖项。如果您尚未添加它,请相应地更新您的类路径。在 MockMvcHtmlUnitDriverCreateMessageTests 中可以找到使用 WebDriver 和 Spring MVC Test 的完整代码示例。
为了使用 WebDriver 和 Spring MVC Test,我们必须首先创建一个MockMvc
实例。关于如何创建MockMvc
实例有很多文档,但我们将在本节中快速回顾一下如何创建MockMvc
实例。
第一步是创建一个新的 JUnit 类,并使用如下所示的注解进行注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {WebMvcConfig.class, MockDataConfig.class})
@WebAppConfiguration
public class MockMvcHtmlUnitDriverCreateMessageTests {
@Autowired
private WebApplicationContext context;
...
}
@RunWith(SpringJUnit4ClassRunner.class)
允许 Spring 对我们的MockMvcHtmlUnitDriverCreateMessageTests
执行依赖注入。这就是为什么我们的@Autowired
注解会被认可的原因。@ContextConfiguration
告诉 Spring 加载哪个配置。您会注意到,我们正在加载数据层的模拟实例以提高测试的性能。如果我们愿意,我们可以选择对真实数据库运行测试。但是,这具有我们之前提到的缺点。@WebAppConfiguration
指示SpringJUnit4ClassRunner
它应该创建一个WebApplicationContext
而不是ApplicationContext
。接下来,我们需要从context
创建我们的MockMvc
实例。下面提供了一个如何执行此操作的示例
@Before
public void setup() {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
...
}
当然,这只是创建MockMvc
实例的一种方法。我们可以选择添加 Servlet 过滤器,使用独立设置等。重要的是我们需要一个MockMvc
的实例。有关创建MockMvc
实例的其他信息,请参阅Spring MVC Test 文档。
现在我们已经创建了MockMvc
实例,我们需要创建一个MockMvcHtmlUnitDriver
,它确保我们使用上一步中创建的MockMvc
实例。
private WebDriver driver;
@Before
public void setup() {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
driver = new MockMvcHtmlUnitDriver(mockMvc, true);
}
现在我们可以像往常一样使用 WebDriver,但无需部署我们的应用程序。例如,我们可以使用以下方法请求创建消息的视图
CreateMessagePage messagePage = CreateMessagePage.to(driver);
然后我们可以填写表单并提交它以创建消息。
ViewMessagePage viewMessagePage =
messagePage.createMessage(ViewMessagePage.class, expectedSummary, expectedText);
这通过利用页面对象模式改进了我们HtmlUnit 测试的设计。正如我们在为什么选择 WebDriver?中提到的,我们可以使用页面对象模式与 HtmlUnit,但现在它更容易了。让我们看看我们的CreateMessagePage
。
public class CreateMessagePage extends AbstractPage {
private WebElement summary;
private WebElement text;
@FindBy(css = "input[type=submit]")
private WebElement submit;
public CreateMessagePage(WebDriver driver) {
super(driver);
}
public <T> T createMessage(Class<T> resultPage, String summary, String details) {
this.summary.sendKeys(summary);
this.text.sendKeys(details);
this.submit.click();
return PageFactory.initElements(driver, resultPage);
}
public static CreateMessagePage to(WebDriver driver) {
driver.get("https://127.0.0.1:9990/mail/messages/form");
return PageFactory.initElements(driver, CreateMessagePage.class);
}
}
您首先会注意到我们的CreateMessagePage
扩展了AbstractPage
。我们不会详细介绍AbstractPage
,但总而言之,它包含了我们所有页面所有通用功能。例如,如果您的应用程序有导航栏、全局错误消息等。此逻辑可以放在共享位置。
接下来,您会发现我们为 HTML 的每个部分(WebElement
)都有一个成员变量,我们对此感兴趣。WebDriver
的PageFactory允许我们通过自动解析每个WebElement
从 HtmlUnit 版本的CreateMessagePage
中删除大量代码。
PageFactory#initElements
方法将通过使用字段名称并尝试通过 HTML 页面上的元素的 id 或名称查找它来自动解析每个WebElement
。我们还可以使用@FindBy 注解来覆盖默认值。我们的示例演示了如何使用@FindBy
注解使用input[type=submit]的 css 选择器查找提交按钮。
最后,我们可以验证新消息是否已成功创建
assertThat(viewMessagePage.getMessage()).isEqualTo(expectedMessage);
assertThat(viewMessagePage.getSuccess()).isEqualTo("Successfully created a new message");
我们可以看到我们的ViewMessagePage
除了单个Message
属性外,还可以返回一个Message
对象。这使我们能够轻松地与丰富的域对象交互,而不仅仅是String
。然后,我们可以在断言中利用丰富的域对象。我们通过创建一个自定义 fest 断言来做到这一点,该断言允许我们验证实际Message
的所有属性是否等于预期的Message
。您可以在Assertions和MessageAssert中查看自定义断言的详细信息
最后,不要忘记在完成后关闭WebDriver
实例。
@After
public void destroy() {
if(driver != null) {
driver.close();
}
}
有关使用 WebDriver 的其他信息,请参阅WebDriver 文档。
WebDriver
拥有与 HtmlUnit 相同的优势,并且额外增加了轻松支持页面对象模式的优势。但是,其中存在相当一部分样板代码可以改进。在下一篇文章中,我们将了解如何使用 Geb 使我们的测试更加 Groovy。
请提供反馈!
如果您对本系列博文或 Spring Test MVC HtmlUnit 有任何反馈,欢迎您通过 github 问题 或在 Twitter 上联系我 @rob_winch。当然,最好的反馈形式是 贡献代码。