必要的“等待”
WebDriver 不跟踪页面 DOM 状态,因此当前操作指令是否可执行将是一个问题,尤其是动态加载的页面。
Web UI 功能测试具有天然的不确定性,测试环境网络、CPU 使用率等方面的状况,都可能引起页面加载缓慢的问题。如果页面/元素当前不可用,直接进行操作就会出现错误。因此,等待操作的前置条件达成,是必要的。
但是,无条件的“硬等待”——等待一个固定的时间——也是不可取的。一方面效率很低,另一方面硬等待并没有消除不确定性,如果等待的时间已到,但条件还是没有达成,就又回到了等待之前的场景。
因此,我们需要的是带条件的等待,一直监控页面,一旦条件达成,就马上开始执行后续操作。
为此,WebDriver 提供了隐式等待和显式等待两种解决方案。
隐式等待
隐式等待,WebDriver 会在一定时间内轮询 DOM 以期找到指定元素。
默认情况下,隐式等待是禁用的,因此元素查询是“立即失败”的。需如下设置:
1 | # 时间以秒为单位 |
【Java 版】
1 driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
需要注意的是,隐式等待对设置后整个会话都有效,这是一个“全局设置”。如果要禁用,重置为 0 即可。
显式等待
隐式等待有以下特性(差不多都是缺点):
- 可配置项少。只有一个可配置选项,即等待时间。
- 功能单一。只影响元素查找行为。
- 影响范围大。设置后整个会话期间都有效。
通常来说,隐式等待只能覆盖某些简单场景,复杂一点的场景就要用到显式等待了。
显式等待允许程序轮询一个条件,直到条件达成,或等待超时。
因此,显式等待需要定义两部分内容:等待对象和条件。
等待对象,用来声明等待配置选项,比如:等待时间、轮询频率、忽略异常类型等。
条件,用来声明等待出现的条件。在不同语言实现中,它可能是个匿名函数、lambda 表达式或者对等物。
示例代码如下:
1 | element = WebDriverWait(driver, timeout=10, ignored_exceptions=[ElementNotVisibleException, ])\ |
【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 版】
混合隐式和显式等待的问题
官方文档警告:不要混合使用隐式和显式等待,会导致不可预测的等待时间。
“坏”结果可能有两个:
- 即使元素可用或条件为真也要等待最长时间。这会影响效率。
- 即使元素可用或条件为真也可能发生超时。这是个错误。