等待元素 - WebDriver - PageObject 模式
只要我使用 PageObject 模式,我就想知道我应该在哪里等待动态页面上的元素.假设我们有测试方法和 pageObject 类.我应该做类似的事情(在测试方法中):
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):
- 点击按钮
- 等待元素显示
- 验证元素(例如包含方法 isElementDisplayed())
或者也许还有其他好的做法来等待元素?也许我们应该等待 PageObject.class 中 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?
推荐答案
你应该在你的页面对象类中等待元素,而不是在测试类中,因为你的元素应该在页面对象类中定义,测试类应该不知道任何元素、选择器或类似的.恕我直言,测试应该只包含描述测试流程的方法调用链,与网站和底层 DOM 的所有交互都应该在 Page Object 类中进行.
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.
因此,等待某个元素出现的过于冗长的方法可能类似于:
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();
}
});
}
简单来说,该函数轮询 DOM 60 秒(每 1 秒)以查看元素是否存在于 DOM 中并且它是否可见(意味着高度和宽度大于 1px).如果元素存在(并且被显示),该函数返回找到的元素并停止轮询(尽管 isLoaded()
方法在这种特殊情况下不返回元素).
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).
忽略NoSuchElementException
是有意义的对元素的引用现在过时"——该元素不再出现在页面的 DOM 上.这通常意味着,某些东西(最常见的是 JS)已经修改了 DOM,并且引用不再有效,因此 WebDriver
需要再次查找它.
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.
当然,更短的代码也可以解决问题,例如:
Of course shorter code would also to the trick, something like:
new WebDriverWait(driver, 60)
.until(ExpectedConditions.visibilityOf(someWebElement));
文档 实际上在这方面做得很好.
The documentation is actually pretty good on this.
回复评论:
好的,明白了.但是如果点击某些元素后存在元素怎么办?按钮等?
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.
}
}
现在你的测试类:
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();
}
}
相关文章