Selenium (Java) 正确使用 PageFactory 的显式等待

Selenium (Java) Correctly using explicit waits with PageFactory

查看 selenium 文档后,我想知道我是否在尝试错误地实现显式等待。

在文档中,它总是显示识别一个新元素,然后将定义的等待分配给所述元素

WebDriver driver = new ChromeDriver();
driver.get("https://google.com/ncr");
driver.findElement(By.name("q")).sendKeys("cheese" + Keys.ENTER);
// Initialize and wait till element(link) became clickable - timeout in 10 seconds
WebElement firstResult = new WebDriverWait(driver, Duration.ofSeconds(10))
        .until(ExpectedConditions.elementToBeClickable(By.xpath("//a/h3")));
// Print the first result
System.out.println(firstResult.getText());

在此示例中,创建了一个新元素 firstResult,然后将定义的等待分配给它。

这是必需的吗?应该总是这样做吗?

这就是我问的原因。

我正在使用 PageFactory 模型并通过 FindBy 注释定义我的元素,如此处所示。

// Input field for slice ID
@FindBy(how = How.XPATH, using = "//input[@name='id']")
private WebElement inputSliceId;

然后,在同一个 class 中,我定义了一些方便的方法来使用它们。

那么现在,在我的便捷方法中,我应该这样做吗?

inputSliceId = new WebDriverWait(driver, Duration.ofSeconds(10))... 
inputSliceId.sendKeys(...

我一直在做的,也是我现在质疑的,是将没有直接分配给相关元素的等待语句。

例如,我一直在做这样的事情。

buttonSubmit.click();
WebDriverWait wait = new WebDriverWait(driver, 5);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//div[@role='alertdialog']")));
wait.until(ExpectedConditions.invisibilityOfElementLocated(By.xpath("//div[@role='alertdialog']")));

为什么? (这里可能完全错了)

点击按钮后,我需要等待弹出窗口显示 一旦它出现,我就会等待它消失,然后再继续

这是主要问题 这两条等待线真的没有做任何事情,因为我没有将它们分配给一个元素吗?还是它们仍然导致网络驱动程序暂停,直到等待指定的条件发生?

不,您不能将上述等待语句分配给您的网络元素。如果您想使用页面工厂模型等待您的元素用于下面的元素,那么您必须创建

public void isLoaded() 抛出错误 { // 初始加载,在创建页面对象时调用,以确保页面加载到准备好与我们交互的状态,在我们的例子中,这意味着按钮出现在 DOM 中并且可见。

public class BaseClass
{
     private void waitForVisibility(WebElement element) throws Error{
               new WebDriverWait(driver, 60)
                    .until(ExpectedConditions.visibilityOf(element));
        }
}

然后在您的页面对象模型中 class 您可以扩展此 BaseClass

public class page extends BaseClass

{
    @FindBy(how = How.XPATH, using = "//input[@name='id']")
    private WebElement inputSliceId;

    waitForVisibility(inputSliceId);

}

我在 BaseClass 中定义了 wait 以实现跨所有页面对象 classes 的 waitForVisibility 代码的可重用性。

此外,如果您想等待弹出窗口出现,请在单击按钮后添加如下代码:

    @FindBy(xpath = "//div[@role='alertdialog']")
    private WebElementFacade alertPopup;

    buttonSubmit.click();
    waitForVisibility(alertPopup);

有几件事:

  • 如果您的用例是在元素上调用 getText(),而不是 elementToBeClickable(),使用 visibilityOfElementLocated() 将是完美的。
  • 要提取文本,您不必创建任何新元素,而是可以在元素返回后直接调用 visibilityOfElementLocated(),如下所示:

    System.out.println(new WebDriverWait(driver, 20).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a/h3"))).getText());
    
  • 如果您的用例不包含对显示和消失的 弹出窗口 的任何验证,您不需要为它并安全地忽略它。因此,一旦您在 buttonSubmit 上调用 click(),您就可以继续下一个验证点,调整 弹出窗口 显示和消失的时间跨度。

  • 因此,您的优化代码块将是:

    buttonSubmit.click();
    // consider the total timespan for the wait to be cumulative of: pop-up displayed + pop-up disappear + next interactable element to become clickable
    new WebDriverWait(driver, Duration.ofSeconds(20)).until(ExpectedConditions.elementToBeClickable(By.xpath("xpath_next_element_to_click")));
    

You can find a relevant detailed discussion in

首先,您可以在单独的 class 中声明一个设置等待本身的方法,像 base 或 parent 一样使用,例如:

protected void waitForElementToBeLoaded(WebElement element) {
 wait.until(ExpectedConditions.elementToBeClickable(element));
}

然后,您可以在单独的 class 中声明使用第一个方法的第二个方法,就像虚构的页面一样:

public void sendMessageForm( ){
    waitForElementToBeLoaded(sendBtn);
    sendBtn.click();
}

最后,每次使用之前的方法都会触发等待,例如:

contactUsPage.sendMessageForm();