何时在Selenium Webdriver中使用显式等待vs隐式等待?

SUM*_*SUM 46 java selenium selenium-webdriver

我在用:

driver.manage().timeouts().implicitlyWait(180, TimeUnit.SECONDS);
Run Code Online (Sandbox Code Playgroud)

但是对于下面的元素,它仍然会不断失败

    driver.findElement(By.id("name")).clear();
    driver.findElement(By.id("name")).sendKeys("Create_title_01");
Run Code Online (Sandbox Code Playgroud)

我添加了等待代码:

for (int second = 0;; second++) {
        if (second >= 120) fail("timeout");
        try { if (isElementPresent(By.id("name"))) break; } catch (Exception e) {}
        Thread.sleep(1000);
    }
Run Code Online (Sandbox Code Playgroud)

不应该隐含等待,直到找到一个元素?如果我使用显式等待而不是我添加的代码,它会更好Thread.sleep()吗?

les*_*ana 92

TL; DR:始终使用显式等待.忘记存在隐式等待.


以下是显式和隐式等待之间差异的快速概述:

明确的等待:

  • 记录和定义的行为.
  • 在selenium的本地部分运行(使用代码的语言).
  • 适用于您能想到的任何条件.
  • 返回成功或超时错误.
  • 可以将元素的缺失定义为成功条件.
  • 可以自定义重试之间的延迟和要忽略的异常.

隐含等待:

  • 没有记录和几乎未定义的行为.
  • 在selenium的远程部分(控制浏览器的部分)中运行.
  • 仅适用于查找元素方法.
  • 返回找到的元素或找不到(超时后).
  • 如果检查元素缺失必须一直等到超时.
  • 除全局超时外无法自定义.

让我们看一下selenium的实际源代码中显式等待和隐式等待之间的区别.我复制了selenium的python绑定代码,因为python"易于阅读".

代码elementToBeClickable(显式等待):

WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
driver.get("http://somedomain/url_that_delays_loading");
WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));
Run Code Online (Sandbox Code Playgroud)

现在使用人类语言:显式等待需要一个方法,如果成功则返回一个值.然后它重复执行给定的方法,其间有延迟.抑制了给定方法的预期误差.如果给定方法返回truish值,则显式wait将返回该值.如果时间用完,则引发超时异常.

与代码比较numberOfElementsToBeMoreThan(为简洁起见,删除了注释):

WebDriver driver = new FirefoxDriver();
driver.get("http://somedomain/url_that_delays_loading");
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement myDynamicElement = wait.until(
  ExpectedConditions.presenceOfElementLocated(By.id("myDynamicElement")));
Run Code Online (Sandbox Code Playgroud)

invisibilityOffindElement*它调用findElement*这反过来做,据我所知,一个RPC硒的远程端.

在人类语言中:隐式等待将消息发送到selenium webdriver的"远程端".selenium webdriver的远程端是selenium的一部分,它实际上控制着浏览器.远程端对消息做了什么?"这取决于".这取决于操作系统和浏览器以及selenium的版本.据我所知,无法保证特定实现的实际行为.

可能的实现是:

  • 反复尝试找到元素直到超时.找到元素后立即返回.
  • 试着寻找元素.等到超时.再试一次.
  • 等到超时.试着寻找元素.

请注意,隐式等待仅对find元素方法生效.

我没有查找selenium远程端的实际源代码.通过阅读关于硒中隐式和显式等待的错误报告中的注释来收集信息:

我的结论:隐性等待很糟糕.能力有限.该行为未记录且依赖于实现.

显式等待可以做一切隐含的等待可以做多.由于多个远程过程调用,显式等待的唯一缺点是更多开销.显式等待也有点冗长.但这种冗长使代码显而易见.隐式的显性更好.对?


进一步阅读:

  • 我发现隐式等待更容易,特别是对于运行本地的机器人或自动化脚本.我在两个不同的项目中使用了显式和隐式,并且隐式代码更快,无需重复自己(DRY).我必须为没有API的Web应用程序创建API!唯一的问题是测试是否存在可能不存在的元素.隐式等待全部时间,但我只是在检查之前将超时设置为0(在try/catch中)并在之后将其重置为5.这种情况发生的次数要少得多,所以值得. (6认同)
  • 如果您的自动化遵循从不检查事物不存在的模式,那么隐式等待只会“容易得多”。这是一个懒惰的选择,它可能会在未来导致许多奇怪且难以诊断的问题(此时大多数人抱怨 Selenium 有多糟糕,没有意识到这是由于他们在创建自动化框架时的选择) (3认同)

eug*_*kov 5

您是否尝试过fluentWait?等待接口的实现,可以动态配置其超时和轮询间隔。每个FluentWait实例都定义了等待条件的最长时间,以及检查条件的频率。此外,用户可以配置等待以在等待时忽略特定类型的异常,例如在页面上搜索元素时的NoSuchElementExceptions。

看到此链接流利的等待描述

特别是我以这种方式使用了流畅的等待:

public WebElement fluentWait(final By locator) {
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
        .withTimeout(30, TimeUnit.SECONDS)
        .pollingEvery(5, TimeUnit.SECONDS)
        .ignoring(NoSuchElementException.class);

    WebElement foo = wait.until(
        new Function<WebDriver, WebElement>() {
            public WebElement apply(WebDriver driver) {
                return driver.findElement(locator);
            }
        }
    );

    return foo;          
};
Run Code Online (Sandbox Code Playgroud)

如您所知,流利的等待返回找到了web元素。因此,您只需通过“类型”传递定位符,然后就可以对找到的Web元素执行任何操作。

fluentWait(By.id("name")).clear();
Run Code Online (Sandbox Code Playgroud)

希望这对您有帮助)


Nas*_*san 2

您是否尝试过使用“ WebDriverWait ”?我想你想要的是这样的:

WebDriverWait _wait = new WebDriverWait(driver, new TimeSpan(0, 0, 2)); //waits 2 secs max
_wait.Until(d => d.FindElement(By.Id("name")));
//do your business on the element here :)
Run Code Online (Sandbox Code Playgroud)

据我了解,这几乎可以完成您当前的代码。它将不断尝试该方法(同时忽略未找到的异常),直到达到传入时间跨度的超时,并且可以输入第三个参数来指定睡眠(以毫秒为单位)。抱歉,如果这也是implicitlyWait 所做的!

编辑:我今天读了一些书,更好地理解了你的问题,并意识到这正是你的隐式等待设置应该做的。将其留在这里,以防代码本身可以帮助其他人。