selenium_waits
Selenium - 等待 (Waits)
Section titled “Selenium - 等待 (Waits)”现代 Web 应用程序通常是动态的。元素可能异步加载、延迟出现,或根据用户操作或后台进程改变状态。如果您的 Selenium 脚本尝试在元素准备好之前与其交互,您将会遇到诸如 NoSuchElementException 或 ElementNotInteractableException 等错误。
Selenium WebDriver 提供了不同的等待策略(waiting strategies)来处理这些时序问题,使您的测试更加可靠和稳定。
为何要避免使用 Thread.sleep()?
Section titled “为何要避免使用 Thread.sleep()?”一种常见但不好的实践是使用固定的暂停时间,例如 Thread.sleep(5000) (Java) 或 time.sleep(5) (Python)。这会强制您的脚本等待一个预定的时间,无论元素是否已经准备好或需要更多时间。这会导致:
- 测试慢: 如果元素很快出现,脚本仍然会进行不必要的等待。
- 测试不稳定 (Flaky): 如果元素加载时间长于固定的等待时间,测试仍然会失败。
- 测试不可靠: 网络条件和应用程序性能各不相同,固定的等待时间不可靠。
切勿在测试中使用 Thread.sleep()(或等效方法)来与浏览器操作同步。 请使用 Selenium 内置的等待机制。
Selenium 中的等待类型
Section titled “Selenium 中的等待类型”1. 隐式等待 (Implicit Wait)
Section titled “1. 隐式等待 (Implicit Wait)”隐式等待(Implicit wait)告诉 WebDriver 在查找任何不是立即可用的元素时,需要轮询(poll)DOM 一段时间。它在每个 WebDriver 会话中设置一次,并全局应用于所有 findElement 和 findElements 调用。
设置 (Java):
// 导入 java.time.Duration 类// Import java.time.Duration;
// 设置隐式等待超时时间 (例如,10 秒)// Set the implicit wait timeout (e.g., 10 seconds)driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));工作原理: 如果您调用 driver.findElement(By.id("myElement")) 且元素没有立即可用,WebDriver 将会持续尝试查找该元素,最长达 10 秒,之后才会抛出 NoSuchElementException。如果元素在此时间内出现,脚本将立即继续执行。
缺点:
- 仅适用于元素在 DOM 中存在,不检查其可见性、可点击性或其他状态。
- 可能通过掩盖较长的加载时间来隐藏性能问题。
- 与显式等待混合使用时,可能导致不可预测的等待时间。
- 通常不推荐使用,建议优先使用显式等待以获得更精确的控制。
2. 显式等待 (Explicit Wait) (推荐)
Section titled “2. 显式等待 (Explicit Wait) (推荐)”显式等待(Explicit wait)使 WebDriver 暂停执行,并轮询 DOM,直到满足特定的 ExpectedCondition 或发生超时。它为特定的元素或条件局部应用,为您提供细粒度的控制。
设置 (Java):
// 导入必要的类// Import necessary classes// 导入 org.openqa.selenium.support.ui.WebDriverWait;// import org.openqa.selenium.support.ui.WebDriverWait;// 导入 org.openqa.selenium.support.ui.ExpectedConditions;// import org.openqa.selenium.support.ui.ExpectedConditions;// 导入 java.time.Duration;// import java.time.Duration;// 导入 org.openqa.selenium.By;// import org.openqa.selenium.By;// 导入 org.openqa.selenium.WebElement;// import org.openqa.selenium.WebElement;
// 创建一个 WebDriverWait 实例 (例如,最多等待 15 秒)// Create a WebDriverWait instance (e.g., wait up to 15 seconds)WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
// 示例 1: 等待直到元素可见// Example 1: Wait until an element is visibleWebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("dynamicElement")));
// 现在与元素进行交互// Now interact with the elementelement.click();
// 示例 2: 等待直到元素可点击// Example 2: Wait until an element is clickableWebElement submitButton = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[@type='submit']")));submitButton.click();
// 示例 3: 等待页面标题包含特定文本// Example 3: Wait for the page title to contain specific textBoolean titleContains = wait.until(ExpectedConditions.titleContains("Results Page"));
// 示例 4: 等待弹窗出现// Example 4: Wait for an alert to be presentAlert alert = wait.until(ExpectedConditions.alertIsPresent());alert.accept();工作原理: wait.until() 方法会定期检查条件(由 ExpectedConditions 或自定义函数定义),检查间隔可配置。如果条件在超时时间内变为真,它会返回结果(例如,WebElement、Boolean、Alert)。如果在达到超时时间之前条件未满足,它会抛出 TimeoutException。
常用的 ExpectedConditions (Java):
visibilityOfElementLocated(By locator): 元素存在且可见。elementToBeClickable(By locator): 元素存在、可见且已启用。presenceOfElementLocated(By locator): 元素存在于 DOM 中(可能不可见)。invisibilityOfElementLocated(By locator): 元素不可见或不在 DOM 中。alertIsPresent(): 弹窗出现。titleContains(String title): 页面标题包含给定文本。textToBePresentInElementLocated(By locator, String text): 元素包含指定的文本。- … 还有很多。
优点:
- 精确: 只需等待满足特定条件所需的时间。
- 灵活: 处理元素存在之外的各种条件。
- 健壮: 通过与应用程序状态适当同步,使测试更加可靠。
- 可读: 清晰地表明脚本在等待什么。
3. 流式等待 (Fluent Wait)
Section titled “3. 流式等待 (Fluent Wait)”FluentWait 是一种显式等待,您可以配置超时持续时间、轮询频率(polling frequency)以及在等待条件时要忽略的异常。与 WebDriverWait(它本质上是一个预配置的 FluentWait)相比,它提供了更多的自定义选项。
设置 (Java):
// 导入必要的类// Import necessary classes// 导入 org.openqa.selenium.support.ui.FluentWait;// import org.openqa.selenium.support.ui.FluentWait;// 导入 org.openqa.selenium.support.ui.Wait;// import org.openqa.selenium.support.ui.Wait;// 导入 org.openqa.selenium.NoSuchElementException;// import org.openqa.selenium.NoSuchElementException;
Wait<WebDriver> fluentWait = new FluentWait<>(driver) // 最大等待时间 // .withTimeout(Duration.ofSeconds(30)) // Max wait time .withTimeout(Duration.ofSeconds(30)) // 每 2 秒检查一次条件 // .pollingEvery(Duration.ofSeconds(2)) // Check condition every 2 seconds .pollingEvery(Duration.ofSeconds(2)) // 在轮询期间忽略此异常 // .ignoring(NoSuchElementException.class); // Ignore this exception during polling .ignoring(NoSuchElementException.class);
// 将 fluentWait 实例与 ExpectedConditions 或自定义函数一起使用// Use the fluentWait instance with ExpectedConditions or a custom functionWebElement element = fluentWait.until(ExpectedConditions.elementToBeClickable(By.id("someId")));element.click();
// 或者使用自定义函数 (Lambda 表达式)// Or using a custom function (Lambda expression)// WebElement customElement = fluentWait.until(d -> d.findElement(By.id("customId")));何时使用: 在特定场景下非常有用,例如需要更精细地控制轮询,或者在等待元素最终出现时需要暂时忽略某些异常(如 NoSuchElementException)。
最佳实践: 优先使用显式等待
Section titled “最佳实践: 优先使用显式等待”对于健壮(robust)和可维护(maintainable)的 Selenium 测试,一般建议是:
- 主要使用显式等待 (
WebDriverWait) 来与特定的元素状态或条件同步。 - 如果广泛使用显式等待,避免使用隐式等待或将其设置为零,以防止不可预测的行为。
- 切勿使用
Thread.sleep()进行同步。
正确实现的等待机制对于创建稳定可靠的自动化 Web 测试至关重要。