隐式等待和显式等待 Selenium 语法

⚡ 智能摘要

Selenium 等待命令将自动化脚本与动态页面行为同步,防止因元素异步加载而导致的测试失败。本参考文档解释了 WebDriver 语法中的隐式、显式和流畅等待策略,并阐述了它们的工作原理。 Java 代码示例,以及关于选择合适的等待时间以实现稳定、可靠的测试执行的实用指导。

  • ⚙️ Foundation基本原则: 全局应用隐式等待,以便 WebDriver 在引发 NoSuchElementException 之前暂停一段定义的时间。
  • 🎯 实施重点: 使用 ExpectedConditions 的显式等待来定位依赖于 Ajax 或动态可见性状态的元素。
  • 🔁 先进技术: 配置 Fluent Wait 的轮询间隔,以处理加载时间不可预测的元素。
  • 优化策略: 用条件等待代替 Thread.sleep,以减少执行时间并消除不稳定的延迟。
  • 质量影响: 选择正确的等待时间可以直接减少不稳定的故障,并提高跨环境脚本的稳定性。

Selenium 等待——语法的隐式和显式

In Selenium“等待”在执行可靠测试中扮演着重要角色。在本教程中,您将学习隐式等待命令和显式等待命令的关键方面和区别。 Selenium以及 Fluent Wait 的实际应用。

为什么我们需要等待 Selenium?

大多数现代 Web 应用程序都是使用以下方式开发的: 阿贾克斯JavaScript当浏览器加载页面时,我们想要交互的元素可能会在不同的时间间隔内加载完毕。

这不仅使元素识别变得困难,而且如果未能及时找到该元素, Selenium 会扔一个 元素不可见异常. Selenium 等待机制通过给 WebDriver 一个确定的时间窗口来解决同步问题,在该窗口内,WebDriver 会查找元素,否则就会失败。

考虑这样一种场景:在同一个测试中同时使用了隐式等待和显式等待。假设隐式等待设置为 20 秒,显式等待设置为 10 秒。

如果我们正在寻找受某些因素支配的元素 预期条件 如果使用显式等待,且目标元素不在 10 秒的显式等待时间内,WebDriver 将回退到 20 秒的隐式等待时间,然后抛出异常。 元素不可见异常.

Selenium WebDriver 等待

  1. 隐式等待
  2. 显式等待
  3. 流利的等待

隐式等待 Selenium

隐式等待 Selenium 指示 WebDriver 在抛出 NoSuchElementException 异常之前等待一段设定的时间。默认值为 0。配置完成后,WebDriver 会在该时间段内轮询 DOM,如果等待时间过长则抛出异常。 Selenium WebDriver 继承了隐式等待的概念。 Watir.

在下面的示例中,我们声明了一个持续时间为 10 秒的隐式等待。如果元素不在该等待时间内,则会引发异常。

隐式等待语法(Selenium 4):

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
package guru.test99;
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.Test;

public class AppTest {

    protected WebDriver driver;

    @Test
    public void guru99tutorials() throws InterruptedException {
        driver = new ChromeDriver();
        driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
        String eTitle = "Demo Guru99 Page";
        String aTitle = "";
        // launch Chrome and redirect it to the Base URL
        driver.get("https://demo.guru99.com/test/guru99home/");
        // Maximize the browser window
        driver.manage().window().maximize();
        // get the actual value of the title
        aTitle = driver.getTitle();
        // compare the actual title with the expected title
        if (aTitle.equals(eTitle)) {
            System.out.println("Test Passed");
        } else {
            System.out.println("Test Failed");
        }
        // close browser
        driver.close();
    }
}

的解释 Code

在上述例子中,请考虑以下陈述:

driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));

Selenium 版本 4 将旧的双参数签名(整数加 TimeUnit)替换为单个 Duration 参数。Duration 类支持秒、分钟、毫秒以及其他时间单位,并通过静态辅助方法(例如 Duration.ofSeconds、Duration.ofMillis 和 Duration.ofMinutes)来实现。

明确等待 Selenium

明确等待 Selenium 指示 WebDriver 在抛出 ElementNotVisibleException 异常之前,等待特定条件(预期条件)或达到最大持续时间。这种等待方式更智能,因为它仅应用于指定的元素。当测试需要处理动态加载的 Ajax 内容时,显式等待优于隐式等待。

一旦声明了显式等待,就必须使用 预期条件或者,您可以使用以下方式配置轮询频率 流利的等待。 避免使用 线程.sleep()因为它会无条件地暂停执行,通常被认为是一种不良做法。

在下面的示例中,我们创建了一个引用 网络驱动程序等待 类,用以下方式实例化它 网络驱动程序 参考,并配置最大窗口时间为 20 秒。

显式等待语法(Selenium 4):

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
package guru.test99;

import java.time.Duration;
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 org.testng.annotations.Test;

public class AppTest2 {
    protected WebDriver driver;

    @Test
    public void guru99tutorials() throws InterruptedException {
        driver = new ChromeDriver();
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
        String eTitle = "Demo Guru99 Page";
        String aTitle = "";
        // launch Chrome and redirect it to the Base URL
        driver.get("https://demo.guru99.com/test/guru99home/");
        // Maximize the browser window
        driver.manage().window().maximize();
        // get the actual value of the title
        aTitle = driver.getTitle();
        // compare the actual title with the expected title
        if (aTitle.contentEquals(eTitle)) {
            System.out.println("Test Passed");
        } else {
            System.out.println("Test Failed");
        }
        WebElement guru99seleniumlink;
        guru99seleniumlink = wait.until(ExpectedConditions.visibilityOfElementLocated(
            By.xpath("/html/body/div[1]/section/div[2]/div/div[1]/div/div[1]/div/div/div/div[2]/div[2]/div/div/div/div/div[1]/div/div/a/i")));
        guru99seleniumlink.click();
    }
}

的解释 Code

考虑以下代码:

WebElement guru99seleniumlink;
guru99seleniumlink = wait.until(ExpectedConditions.visibilityOfElementLocated(
    By.xpath("/html/body/div[1]/section/div[2]/div/div[1]/div/div[1]/div/div/div/div[2]/div[2]/div/div/div/div/div[1]/div/div/a/i")));
guru99seleniumlink.click();

在这个 WebDriverWait 示例中,脚本最多会等待 20 秒,这是由以下代码定义的: 网络驱动程序等待 直到课堂结束 预期条件 无论哪个先发生,双方都感到满意。

以上 Java 代码最多等待 20 秒以符合条件。 元素定位可见性 是真实的。

以下预期条件可用于 Selenium 显式等待:

  1. 警报是否存在()
  2. 元素选择状态要成为()
  3. 可点击元素()
  4. 要选择的元素()
  5. 帧可用并切换到它()
  6. 元素位置的不可见性()
  7. 元素与文本的不可见性()
  8. presenceOfAllElementsLocatedBy()
  9. presenceOfElementLocated()
  10. 元素中要出现的文本()
  11. 元素定位中出现的文本()
  12. 元素值中要出现的文本()
  13. 标题是()
  14. 标题包含()
  15. 可见性()
  16. 可见性所有元素 ()
  17. 可见性所有元素定位于 ()
  18. 元素定位可见性()

流畅等待 Selenium

流畅等待 Selenium Fluent Wait 定义了 WebDriver 等待某个条件的最大持续时间,以及在抛出 ElementNotVisibleException 之前验证该条件的轮询频率。Fluent Wait 会定期检查元素,直到找到该对象或超时为止。

频率: 定期评估病情的重复周期。

设想这样一种场景:某个元素的加载时间无法预测,可能需要 10 秒、20 秒甚至更长时间。如果我们显式地设置 20 秒的等待时间,脚本会一直等待到超时才会失败。在这种情况下,Fluent Wait 是理想的选择,因为它会按照指定的频率轮询,直到找到目标元素或最终的计时器超时为止。

流畅等待语法(Selenium 4):

Wait<WebDriver> wait = new FluentWait<>(driver)
    .withTimeout(Duration.ofSeconds(30))
    .pollingEvery(Duration.ofSeconds(5))
    .ignoring(NoSuchElementException.class);

旧版签名(接受整数值加上 TimeUnit)已被弃用。 Selenium 3.11 并已移除 Selenium 4. 上面所示的基于持续时间的 API 是目前推荐的方法。

package guru.test99;

import java.time.Duration;
import java.util.NoSuchElementException;
import java.util.function.Function;

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.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import org.testng.annotations.Test;

public class AppTest3 {
    protected WebDriver driver;

    @Test
    public void guru99tutorials() throws InterruptedException {
        String eTitle = "Demo Guru99 Page";
        String aTitle = "";
        driver = new ChromeDriver();
        // launch Chrome and redirect it to the Base URL
        driver.get("https://demo.guru99.com/test/guru99home/");
        // Maximize the browser window
        driver.manage().window().maximize();
        // get the actual value of the title
        aTitle = driver.getTitle();
        // compare the actual title with the expected title
        if (aTitle.contentEquals(eTitle)) {
            System.out.println("Test Passed");
        } else {
            System.out.println("Test Failed");
        }

        Wait<WebDriver> wait = new FluentWait<>(driver)
            .withTimeout(Duration.ofSeconds(30))
            .pollingEvery(Duration.ofSeconds(5))
            .ignoring(NoSuchElementException.class);

        WebElement clickseleniumlink = wait.until(new Function<WebDriver, WebElement>() {
            public WebElement apply(WebDriver driver) {
                return driver.findElement(By.xpath("/html/body/div[1]/section/div[2]/div/div[1]/div/div[1]/div/div/div/div[2]/div[2]/div/div/div/div/div[1]/div/div/a/i"));
            }
        });
        // click on the selenium link
        clickseleniumlink.click();
        // close browser
        driver.close();
    }
}

的解释 Code

考虑以下代码:

Wait<WebDriver> wait = new FluentWait<>(driver)
    .withTimeout(Duration.ofSeconds(30))
    .pollingEvery(Duration.ofSeconds(5))
    .ignoring(NoSuchElementException.class);

在上面的例子中,我们声明了一个 Fluent Wait,超时时间为 30 秒,轮询频率为 5 秒,并忽略 没有这样的元素异常.

考虑以下代码:

public WebElement apply(WebDriver driver) {
    return driver.findElement(By.xpath("/html/body/div[1]/section/div[2]/div/div[1]/div/div[1]/div/div/div/div[2]/div[2]/div/div/div/div/div[1]/div/div/a/i"));
}

此函数用于识别页面上的 WebElement(此处为 WebElement)。 Selenium 首页链接)。轮询间隔为 5 秒,超时时间为 30 秒,等待过程每 5 秒检查一次元素,最多持续 30 秒。如果元素在该时间窗口内,脚本继续执行。否则, 元素不可见异常 被抛出。

另请检查: Selenium IDE 初学者教程

隐式等待与显式等待之间的区别

下表重点列出了隐式等待和显式等待之间的主要区别。 Selenium.

隐式等待 显式等待
隐式等待时间应用于脚本中的所有元素。 显式等待时间仅应用于特定目标元素。
在隐式等待中,未为元素指定 ExpectedConditions。 在显式等待中,必须为元素定义 ExpectedConditions。
建议在元素加载于配置的隐式等待窗口内时使用。 建议在元素加载时间较长以及验证元素状态(例如 visibilityOfElementLocated、elementToBeClickable 或 elementToBeSelected)时使用。
设置一次,即可应用于 WebDriver 实例的整个生命周期。 按元素或按检查项设置,提供更精细的控制。

如何选择合适的等待策略

选择正确的等待命令会直接影响脚本的稳定性和执行时间。请使用此决策框架为每种场景选择合适的选项。

  1. 确定负荷模式: 检查元素是在固定时间、Ajax 响应后还是以不可预测的时间间隔出现。固定加载时间适合使用隐式等待 (Implicit Wait),而不可预测的加载时间则需要使用流畅等待 (Fluent Wait)。
  2. 避免混用隐式等待和显式等待: 将两者结合起来可能会造成意想不到的延误,因为较长的等待时间可能会占据主导地位。 Selenium 文档建议每个 WebDriver 会话只使用一种策略。
  3. 默认设置为显式等待动态内容: 当页面依赖于 Ajax、条件渲染或动画时,使用 ExpectedConditions 的显式等待可以精确地定位到重要的元素。
  4. 使用 Fluent Wait 实现可变时序: 当元素加载时间在不同运行之间变化时,请为 Fluent Wait 配置轮询间隔。与单次长时间超时相比,轮询可以减少总等待时间。
  5. 替换 Thread.sleep: 硬编码的睡眠操作会降低测试套件的运行速度,并掩盖真正的时序问题。应将其替换为条件等待,以保持执行速度快且结果确定。
  6. 调整超时值: 开始时设置较短的超时时间(5-10 秒),只有当故障表明负载确实缓慢时才增加超时时间。过长的默认超时时间会掩盖性能下降的问题。
  7. 在辅助工具中保留等待时间: 将等待逻辑集中在一个辅助类中,以便超时策略的更改能够应用于整个测试套件。

遵循这一框架,自动化工程师可以构建能够适应实际应用程序时间安排的弹性脚本,而不会变得缓慢或脆弱。

避免常见错误 Selenium 等待

即使是经验丰富的测试人员,在使用过程中也会陷入一些反复出现的陷阱。 Selenium 等待。避免这些问题可以保证脚本运行快速、稳定,并且易于跨团队和环境维护。

第一个常见的错误是过度使用 `Thread.sleep`。硬编码的暂停会强制脚本等待完整的等待时间,而不管元素是否实际可用,这会增加总运行时间。应该用 `Explicit Wait` 或 `Fluent Wait` 来替换这种暂停,以便脚本在满足条件后立即继续执行。

第二个错误是在同一个测试中混用隐式等待和显式等待。这两种机制会相互叠加,导致难以调试的不可预测的超时。每个 WebDriver 会话应选择一种策略并始终如一地应用。

第三个错误是“以防万一”地将超时时间设置得过长。过长的默认超时时间会掩盖真正的性能问题,并减慢持续集成管道中的反馈速度。应该从较短的超时时间开始,只有当故障日志证实存在真正的时序问题时才延长超时时间。

另请检查: Selenium 初学者教程:7 天学会 WebDriver

常见问题

默认的隐式等待时间 Selenium WebDriver 的等待时间为 0 秒。在调用 implicitlyWait 方法并指定自定义的 Duration 值之前,驱动程序不会因缺少元素而暂停。

Thread.sleep 会无条件地暂停执行一段时间,无论元素是否准备就绪。这会增加整体运行时间,掩盖性能问题,并导致测试不稳定。显式等待和流畅等待会在满足条件时立即恢复执行。

不建议这样做。 Selenium 文档警告称,同时使用这两种策略可能会导致不可预测的等待时间。建议每个 WebDriver 会话选择一种策略,通常是显式等待 (Explicit Wait) 或流畅等待 (Fluent Wait),以获得更清晰的行为和更便捷的调试。

是的。例如,人工智能驱动的测试平台。 TestimMabl 和 Functionize 会分析应用程序响应时间,并推荐显式等待 (Explicit Wait) 或流畅等待 (Fluent Wait) 的值。它们可以减少反复试错的调优过程,并根据观察到的页面行为动态调整超时时间。

AI引擎会学习历史负载模式,并自动调整轮询间隔或超时窗口。当测试在接近时间边界时反复失败,模型会延长等待时间或切换到更智能的条件,从而减少运行过程中出现的不稳定故障。

总结一下这篇文章: