只要我使用 PageObject 模式,我就想知道我应该在哪里等待动态页面上的元素。假设我们有测试方法和 pageObject 类。我应该做类似的事情(在测试方法中):
- 点击按钮
- 等待元素显示
- 验证元素(包含例如方法 isElementDisplayed())
或者也许还有其他好的做法来等待元素?也许我们应该等待 PageObject.class 中 isElementDisplayed 方法中的元素?
只要我使用 PageObject 模式,我就想知道我应该在哪里等待动态页面上的元素。假设我们有测试方法和 pageObject 类。我应该做类似的事情(在测试方法中):
或者也许还有其他好的做法来等待元素?也许我们应该等待 PageObject.class 中 isElementDisplayed 方法中的元素?
你应该在你的页面对象类中等待元素,而不是在测试类中,因为你的元素应该在页面对象类中定义,测试类应该对任何元素、选择器或类似的东西一无所知。恕我直言,测试应该只包含描述测试流程的方法调用链,与网站和底层 DOM 的所有交互都应该在 Page Object 类中进行。
因此,等待某个元素出现的过于冗长的方法可能类似于:
private final By yourElement = By.id("id");
@Override
public void isLoaded() throws Error {
new FluentWait<WebDriver>(driver)
.withTimeout(60, TimeUnit.SECONDS)
.pollingEvery(1, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class)
.ignoring(StaleElementReferenceException.class)
.until(new Function<WebDriver, Boolean>() {
@NotNull
@Override
public Boolean apply(WebDriver webDriver) {
WebElement element = driver.findElement(yourElement);
return element != null && element.isDisplayed();
}
});
}
简而言之,该函数轮询 DOM 60 秒(每 1 秒)以查看元素是否存在于 DOM 中并且它是可见的(意味着高度和宽度大于 1px)。如果元素存在(并显示),则函数返回找到的元素并停止轮询(尽管isLoaded()
在这种特殊情况下方法不返回元素)。
在找不到元素的情况下忽略NoSuchElementException
哪些可以由方法抛出是有意义的,并且 表示对元素的引用现在是“陈旧的”——该元素不再出现在页面的 DOM 上。这通常意味着,某些东西(最常见的是 JS)已经修改了 DOM,并且引用不再有效,因此需要再次查找它。findElement
StaleElementException
WebDriver
当然,较短的代码也可以解决问题,例如:
new WebDriverWait(driver, 60)
.until(ExpectedConditions.visibilityOf(someWebElement));
该文档实际上对此非常好。
编辑:回答评论:
好的,明白了。但是,如果在单击某个按钮等后出现元素怎么办?
假设您有一个场景,您有一个按钮,单击该按钮后会出现一个文本框,您想与它进行交互。
public class PageObject extends LoadableComponent<PageObject>{
public PageObject() throws Exception {
driver = getWebDriver();
PageFactory.initElements(driver, this);
isLoaded();
}
private WebDriver driver = null;
@FindBy(id = "yourButton")
private WebElement button;
@FindBy(id = "textBoxThatAppears")
private WebElement txtBox;
@Override
public void isLoaded() throws Error {
// Initial loading, called when creating the page object to make sure that the page is loaded to a state where it is ready to interact with us, in our case it means that button is present in DOM and visible.
waitForVisibility(button);
}
private void waitForVisibility(WebElement element) throws Error{
new WebDriverWait(driver, 60)
.until(ExpectedConditions.visibilityOf(element));
}
public void clickButton(){
button.click();
}
public void interactWithTextbox(String text){
// Wait for txtBox to be visible, then send text
waitForVisibility(txtBox);
txtBox.sendKeys(text);
// EDIT 27.04.14:
// Actually you should not do the assertion here or anywhere in
// the pageObject, because when reusing the method in some other test, you might
// not want to assert, you might wonder that why wouldn't you assert some
// specific condition every time, but I would throw that question right back
// to you and ask: What is the point of checking the exact same thing over and
// over again. There are 2 things, firstly the assertion takes resources (and
// that can become important when test suite grows, secondly your tests can
// simply start failing at the same point when one little condition is not as
// it should be. Also, having the asserts in the test, makes the test more
// readable and understandable for others.
// end edit 27.04.14
// Next line is no longer recommended by this answer.
// assert that something happened that you expected.
}
}
现在你的测试类:
public void TestClass {
@Test
public void testClickButtonAndInteractWithTextbox(){
// Initiate the page object
Pageobject po = new PageObject();
po.clickButtonAndWaitForTextbox();
po.interactWithTextbox("blabla");
// edit 27.04.14
assertSomethingGoodHappened();
}
}
来自selenium 测试框架之一的另一个有效的测试页面概念(自 selenium 1 起)- ISFW可以在此处使用。它具有延迟加载元素、自定义组件功能和自动等待(不是降低性能的隐式等待)、带有元素的内置等待方法和其他对基于 ajax 的应用程序非常有用的功能。
它为开发测试用例提供了以下构建块:
此外,报告也是描述性的。