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

Posted

技术标签:

【中文标题】何时在 Selenium Webdriver 中使用显式等待与隐式等待?【英文标题】:When to use explicit wait vs implicit wait in Selenium Webdriver? 【发布时间】:2012-05-11 08:40:24 【问题描述】:

我正在使用:

driver.manage().timeouts().implicitlyWait(180, TimeUnit.SECONDS);

但是对于下面的元素它仍然连续失败

    driver.findElement(By.id("name")).clear();
    driver.findElement(By.id("name")).sendKeys("Create_title_01");

我添加了等待码:

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

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

【问题讨论】:

你确定它会以NoSuchElementException 失败吗?它可能会因WebDriverExceptionStaleElementReferenceExceptionElementNotVisibleException 或类似的东西而失败。我会试着猜第一个。 Implicit wait 只等到函数触发 NoSuchElementExceptions... Selenium 显式等待方法和使用 Thread.sleep() 的代码之间的区别在于时间步长:WebDriverWait 每 500 毫秒检查一次元素是否存在,您的代码每 1000 毫秒检查一次。 很抱歉没有说明错误。它是 ElementNotVisibleExeption。但是它抱怨的元素可见异常是由于名称字段为空而无法单击的其他元素。我将添加一个显式等待名称字段,因为这是新页面中要处理的第一个字段。 我有一个相关问题,其中包含来自 Udemy 课程的代码。代码混合了隐式和显式等待。它在执行显式等待之前反复将隐式超时设置为零,并在显式等待结束后将隐式重置回来。这种混合有问题吗?有人可以帮帮我吗 ?目前,我为此提供了 50 赏金,但没有得到可接受的答案。 ***.com/questions/60762906/…。谢谢。 【参考方案1】:

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


以下是显式等待和隐式等待之间区别的简要说明:

显式等待:

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

隐式等待:

未记录且实际上未定义的行为。 在 selenium 的远程部分(控制浏览器的部分)中运行。 仅适用于查找元素方法。 返回找到的元素或(超时后)未找到的元素。 如果检查是否存在元素必须始终等到超时。 除全局超时外无法自定义。

带有解释的代码示例。第一个隐式等待:

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"));

现在显式等待:

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")));

两个代码示例都做同样的事情。找到某个元素,如果 10 秒后没有找到则放弃。隐式等待只能做到这一点。它只能尝试找到一个超时的元素。显式等待的优势在于它可以等待各种条件。还可以自定义超时并忽略某些异常。

可能的条件示例:elementToBeClickablenumberOfElementsToBeMoreThaninvisibilityOf。以下是内置预期条件的列表:https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html

更多解释:

隐式等待超时仅对findElement* 方法有效。如果设置,则所有findElement* 将在声明找不到元素之前“等待”设置的时间。

没有定义findElement* 将如何等待。它取决于浏览器或操作系统或硒的版本。可能的实现有:

反复尝试查找元素,直到超时。找到元素后立即返回。 尝试查找元素。等到超时。再试一次。 等到超时。尝试查找元素。

此列表是通过观察和阅读错误报告以及粗略阅读 selenium 源代码而收集的。


我的结论:隐式等待是不好的。能力是有限的。该行为未记录并依赖于实现。

显式等待可以做所有隐式等待可以做的事情,甚至更多。显式等待的唯一缺点是代码更冗长。但是这种冗长使代码变得明确。显式优于隐式。对吧?


进一步阅读:

Official documentation(除了警告混合隐式和显式等待之外,并没有真正解释问题所在)。 Answer on a related question from Jim Evans。 Jim Evans 是 selenium 的维护者。总结:不要混用隐式等待和显式等待。 两篇博文详细解释了隐式和显式等待: http://vnrtech.blogspot.de/2013/04/selenium-implicit-wait.html http://vnrtech.blogspot.de/2013/04/selenium-explicit-wait.html 已选择bugs about implicit and explicit wait in selenium: http://code.google.com/p/selenium/issues/detail?id=2934 http://code.google.com/p/selenium/issues/detail?id=4471 http://code.google.com/p/selenium/issues/detail?id=7972 代码 explicit wait implicit wait What Happens When We Mix Implicit Wait And Explicit Wait How to create custom expected conditions in Selenium

【讨论】:

我发现隐式等待要容易得多,尤其是对于在本地运行的机器人或自动化脚本。我在两个不同的项目中同时使用了显式和隐式,而隐式编码的速度要快得多,而不必重复自己(DRY)。我必须为没有 API 的 Web 应用程序创建一个 API!唯一的问题是测试是否存在可能不存在的元素。隐式等待全部时间,但我只是在检查之前将超时设置为 0(在 try/catch 中),然后将其重置为 5。这种情况发生的频率要低得多,所以值得。 难道不是显式等待需要两倍的代码吗?这是一个非常糟糕的 API 设计。 如果您的自动化遵循从不检查事物不存在的模式,那么隐式等待只会“容易得多”。这是一个懒惰的选择,它可能会在未来导致许多奇怪且难以诊断的问题(此时大多数人抱怨 Selenium 是多么糟糕,没有意识到这是由于他们在创建自动化框架时的选择) @Ardesco - 你能看看我的回答吗?谢谢。 ***.com/a/60746530/6648326 @Ardesco - 我有一个相关问题,其中包含来自 Udemy 课程的代码。代码混合了隐式和显式等待。它在执行显式等待之前反复将隐式超时设置为零,并在显式等待结束后将隐式重置回来。这种混合有问题吗?请帮我。我为此提供了 50 赏金,但没有得到可接受的答案。谢谢。 ***.com/questions/60762906/….【参考方案2】:

隐式等待 - 全局设置适用于所有元素,如果元素在指定时间之前出现,则脚本将开始执行,否则脚本将抛出 NoSuchElementException。在设置方法中使用的最佳方式。只影响By.findelement()

Thread.sleep() - 脚本会休眠,不是好方法在脚本中使用,因为它是无条件休眠的。如果 5% 的情况下 2 秒不够怎么办?

显式等待:等待指定包含/属性更改。当应用程序向系统发出 AJAX 调用并获取动态数据并在 UI 上呈现时更常用。在这种情况下WebDriverWait 是合适的。

【讨论】:

【参考方案3】:

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

查看此链接fluent wait description

特别是我以这种方式使用流利等待:

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;          
;

您已经注意到流畅的等待返回找到的网络元素。因此,您只需使用 By 类型传递定位器,然后您就可以对找到的 Web 元素执行任何操作。

fluentWait(By.id("name")).clear();

希望对你有帮助)

【讨论】:

Fluent Wait 是显式等待的底层实现【参考方案4】:

您是否尝试过使用 '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 :)

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

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

【讨论】:

你确定吗?哪些/哪里有错误?我以为我在自己的环境中运行了它,一切都很好,所以很感激任何反馈:) 例如方法名称是“findElement”而不是“FindElement”。 “=>”在编写问题的 Java 上下文中没有意义。 @JoshDiehl 答案中的代码是完全正确的 C# 代码。诚然,如果 Nashibukasan 事先提到这一点会很好,并且等效的 Java 代码看起来会有些不同,但这并不意味着代码无效。 啊!谢谢@JimEvans,你是对的,我给出了一个非常刻薄的 C# 示例,我深表歉意!我正在帮助另一个线程中使用 C# 编码并且没有彻底检查您的语法的人。我将尝试在今天的某个时候将我的答案更新为 Java。 WebDriverWait 是显式等待【参考方案5】:

隐式等待:

    1. Static Wait 
    2. UnConditional Wait (No conditions are given)
    3. Applicable throughout the program

在 java 中声明隐式等待 - selenium:

driver.manage().timeout().implicitWait(20, TimeUnit.Seconds());

何时使用隐式等待?

不建议在自动化套件中的任何地方使用隐式等待,因为这是静态的,我们不知道 Web 元素何时会在网站中弹出。

即。假设您设置了 5 秒的隐式等待,并且驱动程序能够在 2 秒内识别 Web 元素,因为我们应用了隐式等待驱动程序将再等待 3 秒(直到 5 秒)。这会减慢自动化进程。

显式等待:

    动态等待 有条件的等待。 不适用于整个计划

在 Java Selenium 中声明显式等待。

WebDriverWait wait=new WebDriverWait(driver, 20); wait.until(somecondition);

何时使用显式等待?

我们应该始终使用显式等待,因为它本质上是动态的。

即。假设您设置了 5 秒的显式等待,并且驱动程序能够在 2 秒内识别 Web 元素,因为我们应用了显式等待驱动程序不会再等待 3 秒(直到 5 秒)。驱动程序将在 2 秒后继续。这将加快自动化过程。

【讨论】:

根据seleniumworkbench.blogspot.com/search/label/dynamic%20wait,隐式等待是动态的 因对隐式等待的误导性解释而被否决,它不会导致每次检索都等待最长持续时间。如果你将隐式等待设置为5秒,则立即找到元素,驱动程序不会等待并立即返回结果,读取1-9,尤其是1和6。它指出,“如果返回的元素为空且当前时间小于结束时间返回第四步,否则继续下一步。 w3c.github.io/webdriver/#element-retrieval【参考方案6】:

隐式等待用于在整个测试脚本或程序的每个连续测试步骤之间提供等待时间(例如 30 秒)。仅在执行上一步后的 30 秒(或给定的任何时间)之后才执行下一步

语法:

WebDriver driver = new FirefoxDriver();
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);

显式等待用于停止执行,直到满足特定条件或定义的最大时间已经过去。隐式等待应用于整个测试脚本或程序的每个连续测试步骤之间,而显式等待仅应用于特定实例。

语法:

WebDriver driver = new FirefoxDriver();
WebDriverWait wait = new WebDriverWait(driver,30);

【讨论】:

【参考方案7】:

在这里查看所有答案和 cmets 后,我用一些代码对它们进行总结,以测试同时使用隐式和显式等待。

仅当您(通常)不需要检查元素是否缺失时才使用隐式等待,例如在一次性网络抓取项目中。

切勿将隐式等待和显式等待混合在一起。请参阅link1 和link2。如果您测试是否缺少元素,则等待时间变得不可预测。在下面的代码中,只有有时等待时间 = 隐式等待。您只需使用无效的定位器即可测试是否缺席。

我已将link2 中的代码进行了重构,使其简短并提供了摘要。该代码显示了同时使用隐式和显式等待时的实际等待时间。

下面的代码会转到一个网站并尝试找到一个有效元素和无效元素。它同时使用隐式和显式等待。在无效元素搜索的情况下,它会尝试隐式/IW 和显式/EW 等待时间的不同组合 - IW = EW, IW > EW 和 IW

一、输出:

WHEN ELEMENT IS FOUND WITHOUT ANY DELAY :
>>> WITH implicit = 30, explicit = 20  :::::  Wait time = 0


WHEN ELEMENT IS NOT FOUND :
a. When implicit wait = explicit wait.
>>> WITH implicit = 10, explicit = 10  :::::  Wait time = 10. ***WITH EXCEPTION*** : NoSuchElementException

b. When implicit wait > explicit wait.
>>> WITH implicit = 30, explicit = 10  :::::  Wait time = 30. ***WITH EXCEPTION*** : NoSuchElementException

c. When implicit wait < explicit wait.
>>> WITH implicit = 10, explicit = 30  :::::  Wait time = 10. ***WITH EXCEPTION*** : NoSuchElementException

代码:

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;

/*
 * Facing this chromedriver error after opening browser - [SEVERE]: Timed out receiving message
 * from renderer: 0.100.
 * */
public class TimeTest 
    static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
    static final String URL = "https://www.redbus.in/";
    static final String TIME_ZONE_NAME = "Europe/Madrid";
    static final By validLoc = By.id("src");
    static final By inValidLoc = By.id("invalid locator");
    static WebDriver driver;

        public static void main(String[] args) 
            dateFormat.setTimeZone(TimeZone.getTimeZone(TIME_ZONE_NAME));

            //>>> Open chrome browser
            System.setProperty("webdriver.chrome.driver", "C:/drivers/chromedriver.exe");
            TimeTest.driver= new ChromeDriver();
            driver.manage().window().maximize();

            //>>> Test waiting logic.

            System.out.println("\n\nWHEN ELEMENT IS FOUND WITHOUT ANY DELAY : ");
            //mixing of implicit wait and explicit wait will not impact on webdriver behavior.
            testWait(30, 20, validLoc, "");

            System.out.println("\n\nWHEN ELEMENT IS NOT FOUND : ");
            //Run the method multiple times. Wait time generally = 10 seconds, but sometimes = 20 seconds.
            testWait(10, 10, inValidLoc, "a. When implicit wait = explicit wait.");

            //Wait time always = implicit wait. Generally ?
            testWait(30, 10, inValidLoc, "b. When implicit wait > explicit wait.");

            //Wait time always = implicit wait. Generally ?
            testWait(10, 30, inValidLoc, "c. When implicit wait < explicit wait.");

            //>>> Close the browser.
            driver.quit();
        


        public static void testWait(int implicitWait, int explicitWait, By locator, String comment)
            // setting implicit time
            driver.manage().timeouts().implicitlyWait(implicitWait, TimeUnit.SECONDS);

            // Loading a URL
            driver.get(URL);

            // defining explicit wait
            WebDriverWait wait= new WebDriverWait(driver, explicitWait);
            // Locating and typing in From text box.


            Date start = new Date();
            String waitStats = comment + "\n>>> WITH implicit = " + implicitWait + ", explicit = " + explicitWait +
                    "  :::::  " ;//+ "Wait start = " + dateFormat.format(start)

            String exceptionMsg = "";

            try 
                WebElement fromTextBox = wait.until(ExpectedConditions.visibilityOf(driver.findElement(locator)));
            catch (Exception ex)
                exceptionMsg = ". ***WITH EXCEPTION*** : " + ex.getClass().getSimpleName();
            

            Date end = new Date();
            //waitStats += ", Wait end = " + dateFormat.format(end)
            waitStats += "Wait time = " +
                    TimeUnit.SECONDS.convert(end.getTime() - start.getTime(), TimeUnit.MILLISECONDS)
                    + exceptionMsg + "\n";

            System.out.println(waitStats);

        


【讨论】:

b 的行为。和 c。在您的示例中,可能会因以下组合而有所不同:您使用的语言绑定、您使用的驱动程序二进制文件以及您使用的前两个版本的哪个版本。无法保证您通过测试获得的结果如何 b.和 c。作品将继续以这种方式进行。行为未定义,取决于您无法控制的情况。 @Ardesco - 为什么它如此不可预测?这是一个错误还是一个功能? 这是不可预测的,因为行为从未被定义,所以没有标准可以编码。这是不断鼓励人们不要将隐式等待和显式等待混为一谈的原因之一。隐式等待是懒惰的捷径,而不是应该在编写良好的测试框架中使用的解决方案。【参考方案8】:

隐式等待:

    适用于所有 driver.findelement 命令 只关心元素的存在。如果元素是不可见的或不可交互的,那么它就不会关心这些。

显式等待

    您可以检查存在性、可见性、可交互性和许多其他内容 - 动态等待这些内容

    WebDriverWait 等待 = new WebDriverWait(driver,Duration.ofSeconds(20)); wait.until(ExpectedConditions.presenceOfElementLocatedBy(By.id("xcxcxc"));

【讨论】:

以上是关于何时在 Selenium Webdriver 中使用显式等待与隐式等待?的主要内容,如果未能解决你的问题,请参考以下文章

Selenium webdriver:修改 navigator.webdriver 标志以防止硒检测

selenium-webdriver 简单教程

如何使用 Java 在 selenium webdriver 中打开新选项卡,或者如何使用 selenium webdriver 使用动作类在 selenium 中按 ctrl + T [重复]

聊聊Selenium不同webdriver的构造

appium的webdriver和selenium有啥区别?

selenium之python源码解读-webdriver继承关系