Selenium和WebDriver(本质上现在是Selenium的继承者)都提供了一种无需人工就可以在多个目标环境中对Web应用程序进行功能测试的好方法。 过去,Web UI是使用页面导航构建的,以允许用户提交表单等。如今,越来越多的Web应用程序使用Ajax,因此其行为和外观与桌面应用程序非常相似。 但是,这给测试带来了问题– Selenium和WebDriver旨在与用户交互配合使用,从而导致页面导航,并且不能与现成的AJAX应用很好地配合使用。
基于GWT的应用程序尤其存在此问题,但是我发现有一些方法可以开发有用且有效的测试。 在模拟用户输入和查找DOM元素方面,GWT还提出了其他问题,我将在下面进行讨论。 请注意,我的代码示例使用Groovy使其简洁,但是可以很容易地将它们转换为Java代码。
问题1:处理异步更改
在测试基于GWT的应用程序时,开发人员很快就会面临的一个问题是检测并等待对用户交互的响应。 例如,用户可以单击导致AJAX调用的按钮,该调用将成功并关闭窗口,或者显示错误消息。 我们需要的是一种阻止方法,直到我们看到预期的变化,并且超时,这样,如果我们看不到预期的变化,我们可能会失败。
解决方案:使用WebDriverWait
最简单的方法是利用WebDriverWait(或Selenium的Wait)。 这使您可以等待条件,并在条件评估为true时继续进行。 下面,为了简洁地使用闭包,我使用了Groovy代码,但是在Java中也可以做到这一点,尽管由于需要匿名类,所以使用了更多代码。
def waitForCondition(Closure closure) {
int timeout = 20
WebDriverWait w = new WebDriverWait(driver, timeout)
w.until({
closure() // wait until this closure evaluates to true
} as ExpectedCondition)
}
def waitForElement(By finder) {
waitForCondition {
driver.findElements(finder).size() > 0;
}
}
def waitForElementRemoval(By finder) {
waitForCondition {
driver.findElements(finder).size() == 0;
}
}
// now some sample test code
submitButton.click() // submit a form
// wait for the expected error summary to show up
waitForElement(By.xpath("//div[@class='error-summary']"))
// maybe some more verification here to check the expected errors
// ... correct error and resubmit
submitButton.click()
waitForElementRemoval(By.xpath("//div[@class='error-summary']"))
waitForElementRemoval(By.id("windowId"))
从示例中可以看到,您的代码可以专注于实际的测试逻辑,同时无缝地处理GWT应用程序的异步特性。
问题2:在您对DOM几乎没有控制的情况下定位元素
在使用模板的Web应用程序(JSP,Velocity,JSF等)中,您可以很好地控制并轻松查看页面将具有的DOM结构。 对于GWT,情况并非总是如此。 通常,您正在处理无法精确控制的嵌套元素。
使用WebDriver和Selenium,可以使用几种方法来定位元素,但最有用的是DOM元素ID和XPath。 我们如何利用它们来获得可维护的测试,而这些测试不会因布局的微小变化而中断?
解决方案:结合使用XPath和ID来限制范围
以我的经验,要在WebDriver中开发功能性GWT测试,您应该使用稍微松散的XPath作为查找元素的主要方法,并在适用时通过DOM ID对这些调用进行作用域来对其进行补充。
特别是,请在应用程序中唯一的窗口或选项卡等顶级元素上使用ID,这些ID在页面中不会出现多次。 这些可以帮助确定您的XPath表达式的范围,该表达式可以查找窗口或表单标题,字段标签等。
以下是一些示例,可助您一臂之力。 请注意,我们在XPath中使用//和*来保持表达式的灵活性,以便除非主要更改布局更改,否则不会破坏我们的测试。
By byUserName = By.xpath("//*[@id='userTab']//*[text()='User Name']/..//input")
WebElement userNameField = webDriver.findElement(byUserName)
userNameField.sendKeys("my new user")
// maybe a user click and then wait for the window to disappear
By submitLocator = By.xpath("//*[@id='userTab']//input[@type='submit']")
WebElement submit = webDriver.findElement(submitLocator)
submit.click()
// use our helper method from Problem 1
waitForElementRemoval By.id("userTab")
问题3:法线元素交互方法不起作用!
就管理DOM的状态而言,GWT及其派生工具(Vaadin,GXT等)通常在幕后发挥作用。 对开发人员来说,这意味着您不必总是处理普通的<input>或<select>等元素。 仅通过常规方法简单地设置字段的值可能不起作用,并且使用WebDriver或Selenium的click方法可能不起作用。
WebDriver在这方面有所改进,但是问题仍然存在。
解决方案:不幸的是,只有一些解决方法
您可能会遇到的主要问题与在字段中键入和单击元素有关。
以下是一些我过去发现有必要的变体,可以避免点击无法正常运行。 如果遇到问题,请尝试一下。 这些示例在Selenium中,但是如果需要,可以将它们改编为适用于WebDriver中的相应调用。 如果您想直接使用示例,也可以将Selenium适配器用于WebDriver(WebDriverBackedSelenium)。
点击问题
有时,元素不会响应Selenium或WebDriver中的click()调用。 在这些情况下,通常必须在浏览器中模拟事件。 Selenium在2.0之前比WebDriver更是如此。
// Selenium's click sometimes has to be simulated with events.
def fullMouseClick(String locator) {
selenium.mouseOver locator
selenium.mouseDown locator
selenium.mouseUp locator
}
// In some cases you need only mouseDown, as mouseUp may be
// handled the same as mouseDown.
// For example, this could result in a table row being selected, then deselected.
def mouseOverAndDown(String locator) {
selenium.mouseOver locator
selenium.mouseDown locator
}
打字问题
这些是过去在GWT无法识别键入的输入时能够成功使用的回旋输入法。
// fires only key events (works for most GWT inputs)
// Useful if WebDriver sendKeys() or Selenium type() aren't cooperating.
def typeWithEvents(String locator, String text) {
def keyEvents = ["keydown", "keypress", "keyup"]
typeWithEvents(locator, text, keyEvents)
}
// fires key events, plus blur and focus for really picky cases
def typeWithFullEvents(String locator, String text) {
def fullEvents = ["keydown", "keypress", "keyup", "blur", "focus"]
typeWithEvents(locator, text, fullEvents)
}
// use this directly to customize which events are fired
def typeWithEvents(String locator, String text, def events) {
text.eachWithIndex { ch, i ->
selenium.type locator, text.substring(0, i+1)
events.each{ event ->
selenium.fireEvent locator, event
}
}
}
请注意,必须通过反复试验找出有效的方法,在某些情况下,您在不同的浏览器中可能会得到不同的行为,因此,如果针对不同的环境运行功能测试,则必须确保您的方法适用于所有这些方法。
结论
希望你们中的一些人发现这些技巧有用。 那里也有类似的技巧,但我想汇编一套很好的示例和变通方法,以使处于类似情况的其他人不会陷入僵局,也不会在需要大量猜测和时间的问题上浪费时间。
参考: Carfey Software博客上的JCG合作伙伴提供的使用Selenium或WebDriver测试GWT应用程序 。
翻译自: https://www.javacodegeeks.com/2011/10/testing-gwt-apps-with-selenium-or.html