Java Selenium WebDriver:当无法使用 WebDriver.findElement 定位时,等待元素出现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19851518/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 20:42:02  来源:igfitidea点击:

Selenium WebDriver: wait for element to be present when locating with WebDriver.findElement is impossible

javaseleniumselenium-webdriver

提问by user2432405

It's convenient to wait for an WebElementto be present with WebDriverWaitand ExpectedConditions.

等待 anWebElementWebDriverWaitand一起出现很方便ExpectedConditions

The problem is, what if WebElement.findElmentwas the only possible way to locate the element , 'cause it has no id, no name, no unique class?

问题是,如果WebElement.findElment是定位元素的唯一可能方法,因为它没有 id、没有名称、没有唯一的类怎么办?

WebDriverWait's constructor accepts only WebDriveras arguments, not WebElement.

WebDriverWait的构造函数只接受WebDriver作为参数,而不接受WebElement

I've set the implicitlyWaittime, so it seems not a good idea to use try{} catch(NoSuchElementException e){}, 'cause I don't want to wait that long time for this element.

我已经设置了implicitlyWait时间,所以使用 似乎不是一个好主意try{} catch(NoSuchElementException e){},因为我不想为这个元素等待那么长时间。

Here's the scenario:

这是场景:

There's one web page with a form containing many inputtags. Each inputtag has a format requirement.

有一个包含许多input标签的表单的网页。每个input标签都有格式要求。

A dynamic divtag would be present after this inputtag when the format requirement is not satisfied.

当不满足格式要求时,该div标签后将出现一个动态标签input

As there're so many inputtags, I create a general method like:

由于input标签太多,我创建了一个通用方法,例如:

public WebElement txtBox(String name) {
    return driver.findElement(By.name(name));
}

instead of creating a data member for each inputtag.

而不是为每个input标签创建一个数据成员。

Then I create a method isValidto check whether user inputs in some inputare valid. All I should do in isValidis to check whether a divtag is present after inputboxToCheck, with code like this:

然后我创建了一个方法isValid来检查用户输入是否input有效。我应该做的isValid就是检查div在 之后是否存在标签inputboxToCheck,代码如下:

public boolean isValid(WebElement inputboxToCheck) {
    WebElementWait wait = new WebElementWait(inputboxToCheck, 1);
    try {
        wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("./following-sibling::div")));
        return false;
    } catch (TimeOutException e) {
        return true;
    }    
}

WebElementWaitis an imaginary (not exist) class which works the same way as WebDriverWait.

WebElementWait是一个虚构的(不存在的)类,其工作方式与WebDriverWait.

采纳答案by user2432405

The WebElementWait class as metioned above:

上面提到的 WebElementWait 类:

package org.openqa.selenium.support.ui;

import java.util.concurrent.TimeUnit;

import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.WebElement;

public class WebElementWait  extends FluentWait<WebElement>  {
    public final static long DEFAULT_SLEEP_TIMEOUT = 500;

      public WebElementWait(WebElement element, long timeOutInSeconds) {
            this(element, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, DEFAULT_SLEEP_TIMEOUT);
      }

      public WebElementWait(WebElement element, long timeOutInSeconds, long sleepInMillis) {
            this(element, new SystemClock(), Sleeper.SYSTEM_SLEEPER, timeOutInSeconds, sleepInMillis);
      }

      protected WebElementWait(WebElement element, Clock clock, Sleeper sleeper, long timeOutInSeconds,
              long sleepTimeOut) {
            super(element, clock, sleeper);
            withTimeout(timeOutInSeconds, TimeUnit.SECONDS);
            pollingEvery(sleepTimeOut, TimeUnit.MILLISECONDS);
            ignoring(NotFoundException.class);
      }

}

It's the same as WebDriverWait, except that the WebDriverargument is replaced with WebElement.

它与 WebDriverWait 相同,只是WebDriver参数被替换为WebElement.

Then, the isValid method:

然后是 isValid 方法:

//import com.google.common.base.Function;
    //import org.openqa.selenium.TimeoutException;

public boolean isValid(WebElement e) {
    try {
        WebElementWait wait = new WebElementWait(e, 1);
        //@SuppressWarnings("unused")
        //WebElement icon = 
        wait.until(new Function<WebElement, WebElement>() {
                    public WebElement apply(WebElement d) {
                        return d.findElement(By
                                .xpath("./following-sibling::div[class='invalid-icon']"));
                    }
                });
        return false;
    } catch (TimeoutException exception) {
        return true;
    }
}

回答by Andrian Durlestean

I don't know if this help you, but it permits to wait element how much time do you want.

我不知道这是否对你有帮助,但它允许等待元素你想要多长时间。

public WebElement findDynamicElement(By by, int timeOut) {
    WebDriverWait wait = new WebDriverWait(driver, timeOut);
    WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(by));
    return element;
}

findDynamicElement(By.xpath("//body") , 30);

回答by Eric Francis

I found this blog: Checking for an element – exists?, visible?, present? - https://jkotests.wordpress.com/2012/11/02/checking-for-an-element-exists-visible-present/

我找到了这个博客:检查元素 – 存在吗?可见吗?存在吗?- https://jkotests.wordpress.com/2012/11/02/checking-for-an-element-exists-visible-present/

And it brought up the differences between exists, visible, and present.

它提出了存在、可见和现在之间的差异。

  • exists? – Returns whether this element actually exists.
  • present? – Returns true if the element exists and is visible on the page
  • visible? – If any parent element isn't visible then we cannot write to the element. The only reliable way to determine this is to iterate up the DOM element tree checking every element to make sure it's
    visible.
  • 存在吗?– 返回此元素是否实际存在。
  • 展示?– 如果元素存在并且在页面上可见,则返回 true
  • 可见的?– 如果任何父元素不可见,则我们无法写入该元素。确定这一点的唯一可靠方法是迭代 DOM 元素树,检查每个元素以确保它是
    可见的。

Exists will tell you if what you are searching for is anywhere in the DOM; however, WebDriverdoes not seem to have a built in way to check if an element exists similar to plain driver.findElement(By.name(name)).

Exists 会告诉您您要搜索的内容是否在 DOM 中的任何位置;但是,WebDriver似乎没有内置的方法来检查是否存在类似于 plain 的元素driver.findElement(By.name(name))

And, as explained in the blog, Existsis not the same as Present. So I can't use ExpectedConditions.presenceOfAllElementLocatedBy(By.cssSelector(cssSelector)

而且,正如博客中所解释的,ExistsPresent 不同。所以我不能用ExpectedConditions.presenceOfAllElementLocatedBy(By.cssSelector(cssSelector)

My solution: (looking for feedback here :)

我的解决方案:(在这里寻找反馈:)

public WebElement waitForElementExists(String selector, String timeout) {
    Wait<WebDriver> wait = new WebDriverWait(driver, timeout);

    WebElement element = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver driver) {
            return driver.findElement(By.cssSelector(selector));
        }
    });

    return element;
 }

回答by Nik

A more universal variant of user2432405's solution would be using SearchContext type rather then WebElement:

user2432405 解决方案的一个更通用的变体是使用 SearchContext 类型而不是 WebElement:

public class SearchContextWait  extends FluentWait<SearchContext>  {
    ...

This allows to do waits on both WebDriver and WebElement similarly as the SearchContext interface is the ancestor of both WebDriver and WebElement. The isValid method needs adjustment too:

这允许在 WebDriver 和 WebElement 上进行等待,类似于 SearchContext 接口是 WebDriver 和 WebElement 的祖先。isValid 方法也需要调整:

...
        WebElement icon = wait
                .until(new Function<SearchContext, WebElement>() {
                    public WebElement apply(SearchContext d) {
...

Unfortunately, you lose all conveniences of ExpectedConditions.xxxx() methods as they use the WebDriver interface internally.

不幸的是,您失去了 ExpectedConditions.xxxx() 方法的所有便利,因为它们在内部使用 WebDriver 接口。