0%

Selenium4学习笔记:等待

必要的“等待”

WebDriver 不跟踪页面 DOM 状态,因此当前操作指令是否可执行将是一个问题,尤其是动态加载的页面。

Web UI 功能测试具有天然的不确定性,测试环境网络、CPU 使用率等方面的状况,都可能引起页面加载缓慢的问题。如果页面/元素当前不可用,直接进行操作就会出现错误。因此,等待操作的前置条件达成,是必要的。

但是,无条件的“硬等待”——等待一个固定的时间——也是不可取的。一方面效率很低,另一方面硬等待并没有消除不确定性,如果等待的时间已到,但条件还是没有达成,就又回到了等待之前的场景。

因此,我们需要的是带条件的等待,一直监控页面,一旦条件达成,就马上开始执行后续操作。

为此,WebDriver 提供了隐式等待和显式等待两种解决方案。

隐式等待

隐式等待,WebDriver 会在一定时间内轮询 DOM 以期找到指定元素。

默认情况下,隐式等待是禁用的,因此元素查询是“立即失败”的。需如下设置:

1
2
# 时间以秒为单位
driver.implicitly_wait(10)

【Java 版】

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

需要注意的是,隐式等待对设置后整个会话都有效,这是一个“全局设置”。如果要禁用,重置为 0 即可。

显式等待

隐式等待有以下特性(差不多都是缺点):

  • 可配置项少。只有一个可配置选项,即等待时间。
  • 功能单一。只影响元素查找行为。
  • 影响范围大。设置后整个会话期间都有效。

通常来说,隐式等待只能覆盖某些简单场景,复杂一点的场景就要用到显式等待了。

显式等待允许程序轮询一个条件,直到条件达成,或等待超时。

因此,显式等待需要定义两部分内容:等待对象和条件。

等待对象,用来声明等待配置选项,比如:等待时间、轮询频率、忽略异常类型等。

条件,用来声明等待出现的条件。在不同语言实现中,它可能是个匿名函数、lambda 表达式或者对等物。

示例代码如下:

1
2
element = WebDriverWait(driver, timeout=10, ignored_exceptions=[ElementNotVisibleException, ])\
.until(lambda d: d.find_element(By.ID, 'test'))

【Java 版】

1
2
3
var wait = new WebDriverWait(driver, 
Duration.ofSeconds(10), Duration.ofSeconds(1)).ignoring(NoSuchElementException.class);
var element = wait.until(d -> d.findElement(By.id("test")));

Selenium 为一些常用条件提供了快捷的生成方法,可参考 selenium.webdriver.support.expected_conditions

【Java 版】

工具类为 org.openqa.selenium.support.ui.ExpectedConditions

混合隐式和显式等待的问题

官方文档警告:不要混合使用隐式和显式等待,会导致不可预测的等待时间。

“坏”结果可能有两个:

  • 即使元素可用或条件为真也要等待最长时间。这会影响效率。
  • 即使元素可用或条件为真也可能发生超时。这是个错误。