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

总结一下这篇文章: