Java 等待元素 - WebDriver - PageObject 模式
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18843581/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Wait for element - WebDriver - PageObject pattern
提问by Robert
As long as I use PageObject pattern I wondered where should I wait for element on dynamic pages. Assuming we have test method and pageObject class. Should I do something like (in test method):
只要我使用 PageObject 模式,我就想知道我应该在哪里等待动态页面上的元素。假设我们有测试方法和 pageObject 类。我应该做类似的事情(在测试方法中):
- Click on button
- Wait for element to be displayed
- Verify the element (contains eg. method isElementDisplayed())
- 点击按钮
- 等待元素显示
- 验证元素(包含例如方法 isElementDisplayed())
Or maybe there is other good practice to wait for the element? Maybe we should wait for element in method isElementDisplayed which is in PageObject.class?
或者也许还有其他好的做法来等待元素?也许我们应该等待 PageObject.class 中的 isElementDisplayed 方法中的元素?
采纳答案by Erki M.
You should wait for elements in your page object class, not in test class, because your elements should be defined in page object class, test class should know nothing of any elements, selectors or similar. Tests, IMHO, should contain only chains of method calls that describe the test flow, all the interaction with the website and underlying DOM should take place in Page Object class.
您应该等待页面对象类中的元素,而不是测试类中的元素,因为您的元素应该在页面对象类中定义,测试类不应该知道任何元素、选择器或类似元素。恕我直言,测试应该只包含描述测试流程的方法调用链,所有与网站和底层 DOM 的交互都应该在 Page Object 类中进行。
So an overly verbose method to wait for some element to appear could be something like:
因此,等待某个元素出现的过于冗长的方法可能是这样的:
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();
}
});
}
In plain words, the function if polling the DOM for 60 secs (every 1 second) to see, if the element exists in DOM and it is visible (means has height and witdh greater than 1px). If the element exists (and is displayed), the function returns the found element and stops the polling (although isLoaded()
method does not return the element in this particular case).
简单来说,该函数如果轮询 DOM 60 秒(每 1 秒)以查看元素是否存在于 DOM 中并且它是可见的(意味着具有大于 1px 的高度和宽度)。如果该元素存在(并显示),则该函数返回找到的元素并停止轮询(尽管isLoaded()
在这种特殊情况下方法不返回该元素)。
It makes sense to ignore NoSuchElementException
which can be thrown by findElement
method in case the element is not found, and StaleElementException
, which indicates that a reference to an element is now "stale" - the element no longer appears on the DOM of the page. This usually means, that something (most commonly JS) has modified the DOM and the reference is no longer valid, hence the WebDriver
needs to look it up again.
如果找不到元素NoSuchElementException
,可以忽略findElement
方法抛出 which是有意义的, andStaleElementException
表示对元素的引用现在“过时” - 该元素不再出现在页面的 DOM 上。这通常意味着,某些东西(最常见的是 JS)修改了 DOM 并且引用不再有效,因此WebDriver
需要再次查找它。
Of course shorter code would also to the trick, something like:
当然,更短的代码也很重要,比如:
new WebDriverWait(driver, 60)
.until(ExpectedConditions.visibilityOf(someWebElement));
The documentationis actually pretty good on this.
该文件实际上是对这个很不错。
EDIT: answer to the comment:
编辑:回答评论:
OK, understood. But what if element is present after clicking on some button etc.?
好的,明白了。但是如果在单击某个按钮等后元素存在怎么办?
Lets say you have a scenario, where you have a button and after clicking that button a textbox appears and you want to interact with it.
假设您有一个场景,其中有一个按钮,单击该按钮后会出现一个文本框,您想与其进行交互。
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.
}
}
And now your test class:
现在你的测试课:
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();
}
}
回答by user861594
Another efficient concept of test page(Since selenium 1) from one of the selenium testing-frameworks- ISFWcan be utilized over here. It has lazy loaded element, custom component feature and auto wait (not implicit wait that reduce performance), inbuilt wait methods with element and other features that are very useful for ajax bases application.
来自硒测试框架之一的测试页面(自 selenium 1 起)的另一个有效概念- ISFW可以在这里使用。它具有延迟加载元素、自定义组件功能和自动等待(不是降低性能的隐式等待)、带有元素的内置等待方法以及对 ajax 基础应用程序非常有用的其他功能。
It provide following building blocks for developing test case:
它为开发测试用例提供了以下构建块:
- Test Page
- Component
- Test step
- 测试页
- 成分
- 测试步骤
In addition Reportingis also descriptive.
此外,报告也是描述性的。