强制等待与隐式等待
同学们大家好,今天这个章节,我们来学习一下有关于Web自动化测试中,一个老生常谈的话题,等待的问题。
简介
在自动化测试中,等待机制用于确保测试脚本可以正确执行,避免因页面加载、网络延迟或动画效果导致的测试失败。常见的等待方式包括强制等待和隐式等待。
在这一章节,我们将介绍 Selenium 中的三种常见等待机制,分别是强制(直接)等待、隐式等待和显式等待。等待机制在自动化测试中非常重要,因为它能帮助我们处理页面加载时间、元素渲染延迟等问题,确保测试脚本的稳定性和可靠性。强制等待让我们能够在脚本中强制暂停一段时间,虽然这种方式简单直接,但它可能会导致不必要的等待时间。隐式等待则是为所有的元素查找设置一个统一的等待时间,在这个时间范围内,如果元素没有找到,Selenium 会继续尝试查找,直到超时。显式等待则是更加灵活的方式,它允许我们为特定的元素设置等待条件,只有在元素符合指定条件时,脚本才会继续执行。这三种等待方式各有优缺点,我们将在后续的讲解中逐一分析并展示如何使用它们,帮助你们在实际测试中更高效地应对不同的页面加载和元素查找问题。
使用场景
使用等待就是为了避免页面未渲染完成时就操作,从而导致报错。
- 等待动画或过渡效果完成
- 处理某些特定的固定延迟(如弹窗消失、页面重定向等)
- 调试测试脚本
在自动化测试中,添加等待是非常重要的,因为页面加载和元素渲染通常需要一定时间,如果在页面未完全加载时就尝试对元素进行操作,可能会导致脚本报错。比如某个页面还没有完全加载,元素还没有渲染出来,直接操作该元素脚本会抛出 NoSuchElementException 异常,这会导致测试失败。为了避免这种情况,我们需要在脚本中合理地添加等待时间,确保在操作元素之前,页面上的元素已经完全加载和渲染,避免因为页面未渲染完成就进行操作而导致的报错。接下来的章节我们会详细介绍如何通过强制等待、隐式等待和显式等待来处理这种情况,并让你的自动化测试脚本更稳定、高效。
强制等待
强制等待是指让测试脚本暂停执行指定的时间,无论页面是否加载完成,它都会等待设定的时间再执行下一步操作。
- 原理:强制等待,线程休眠一定时间。
- python:
time.sleep(3000)
(单位是毫秒) - java:
Thread.sleep(3000);
(单位是毫秒)
直接等待,也称为强制等待或者死等,是三种等待方式中最简单的一种等待方式,它是通过直接让线程休眠的方式使脚本暂停执行一段固定的时间,确保页面有足够的时间完成加载。它的基本原理是使用 Thread点sleep 方法,在代码中插入一个强制的休眠时间,以避免页面未渲染完成就执行操作导致的错误。不过需要注意的是,强制等待的时间是固定的,也就是说即使页面在等待时间之前已经加载完成,脚本也会继续等待,所以就可能造成不必要的时间浪费。同时呢,如果页面加载时间超过了指定的等待时间,脚本还是可能会出错。因此,强制等待虽然简单易用,但并不是一种高效的等待机制,而是适合在调试或者非常简单的场景下使用。在实际项目中,我们更推荐大家使用隐式等待或显式等待,这两种方式能够更加智能地处理页面加载问题,并且只在必要时等待,提升测试脚本的执行效率和稳定性。
python 示例
from selenium import webdriver
from selenium.webdriver.common.by import By
def wait_sleep():
"""
如果直接执行,不添加任何等待,可能会报错
"""
driver = webdriver.Chrome()
driver.get("https://vip.ceshiren.com/")
# 添加等待,让页面渲染完成
time.sleep(3)
driver.find_element(By.XPATH, "//*[text()='个人中心']")
if __name__ == '__main__':
wait_sleep()
java 示例
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import java.time.Duration;
public class WaitTest {
public static WebDriver driver;
@BeforeAll
static void setUpAll() {
driver = new ChromeDriver();
}
@AfterAll
static void tearDownAll() {
driver.quit();
}
@Test
void waitSleep() throws InterruptedException {
driver.get("https://vip.ceshiren.com/");
Thread.sleep(3000);
driver.findElement(By.xpath("//*[text()='个人中心']"));
}
}
隐式等待
隐式等待告诉 WebDriver 在查找元素时,如果找不到元素,则在超时时间范围内不断轮询查找,直到元素出现或超时。适用于页面加载时间不固定、元素可能会在一段时间后才出现,或者是需要全局适用的等待策略。
- 原理:设置一个等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常。
- python:
driver.implicitly_wait(10)
- java:
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
再来说隐式等待,隐式等待是一种更智能的等待方式,用于解决无法确定元素加载具体时间的问题。通过为 WebDriver 设置一个全局的等待时间,在这个时间内,Selenium 会以默认 0.5 秒的轮询频率不断检查目标元素是否加载完成,如果元素在超时时间内出现,脚本会立即继续执行;如果超时后仍未找到元素,则抛出异常。比如这段代码; 我们为 WebDriver 配置了 10 秒的隐式等待时间,这意味着在所有的元素查找操作中,Selenium 都会尝试在 10 秒内查找到目标元素,而不是立即抛出错误。隐式等待特别适合页面加载时间不可控,但是多数元素加载时间相对一致的场景,它可以大幅提升测试脚本的容错性和稳定性,同时呢也让代码更加简洁。然而需要注意的是,隐式等待是全局生效的,也就是说不管哪个场景,是加载快还是加载慢的场景,都会被覆盖到,因此在需要更细粒度等待的场景中,可以结合显式等待使用,从而更灵活地控制脚本的执行逻辑。
强制等待 vs. 隐式等待
对比项 | 强制等待(Sleep) | 隐式等待(Implicit Wait) |
---|---|---|
等待方式 | 固定时间,无论元素是否加载完成 | 设定时间内轮询查找元素,找到后立即执行 |
适用场景 | 适用于短暂等待(如动画、重定向) | 适用于页面加载时间不确定的情况 |
执行效率 | 低,可能造成不必要的等待 | 高,减少等待时间,提高测试效率 |
隐式等待无法解决的问题
- 元素可以找到,使用点击等操作,出现报错
- 原因: - 页面元素加载是异步加载过程,通常html会先加载完成,js、css其后 - 元素存在与否是由HTML决定,元素的交互是由css或者js决定 - 隐式等待只关注元素能不能找到,不关注元素能否点击或者进行其他的交互
- 解决方案:使用显式等待
隐式等待虽然能够解决元素加载时间不确定的问题,但在某些情况下并不能满足所有需求,例如元素可以找到,但在尝试进行点击或其他交互时仍然会报错。那么为什么会出现这种问题呢,原因在于,页面的加载是一个异步的过程,通常 HTML 结构会先加载完成,而 CSS 和 JavaScript 的加载可能会滞后。隐式等待只会关注元素是否存在于 DOM 中,这是由 HTML 决定的,但元素的交互能力(例如是否可点击)通常由 CSS 或 JavaScript 决定。也就是说,隐式等待无法判断元素是否处于可交互的状态,比如按钮可能已经在页面上渲染出来,但由于 JavaScript 尚未加载完成,按钮的点击事件未绑定,此时进行点击操作就会报错。所以为了解决这些问题,可以使用显式等待,显式等待允许我们设置更精细的条件,比如等待元素可见、可点击等,它能够在真正满足条件时再执行后续操作,从而更有效地避免交互失败的问题。
显式等待基本使用(初级)
- 原理:在最长等待时间内,是否满足结束条件
- 基本使用:
WebDriverWait(driver实例, 最长等待时间).until(结束条件);
python 示例
java 示例
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
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 java.time.Duration;
public class WaitTest {
public static WebDriver driver;
@BeforeAll
static void setUpAll() {
driver = new ChromeDriver();
//显式等待
driver.manage().timeouts().implicitlyWait(
Duration.ofSeconds(10));
}
@AfterAll
static void tearDownAll() {
driver.quit();
}
@Test
void wait_untils(){
driver.get("https://vip.ceshiren.com/#/ui_study");
WebElement resElement = new WebDriverWait(driver,
Duration.ofSeconds(10))
.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("#success_btn")));
resElement.click();
}
}
显式等待是一种更加灵活和精确的等待方式,它可以在设定的最大等待时间内持续检测指定的条件是否满足,当条件满足时立即执行后续操作,若超时则抛出异常。在示例中,我们访问了学员系统,通过显式等待等待页面上按钮 #success_btn 变为可点击状态,具体实现可以参考右侧的代码, 设置最长等待时间为 10 秒,并且设置判断元素是否可以点击作为结束条件,这样就可以确认目标按钮可以点击后再执行点击操作。显式等待的优势在于可以灵活地根据需求设置条件,比如等待元素可见、可点击或特定属性变化等,特别适合页面加载复杂并且需要确保特定交互条件的场景,是解决隐式等待局限性的重要工具。对于初学者来说,显式等待的重点在于理解其基本用法和原理,并熟悉常见的结束条件,为后续的深入应用打好基础。
总结
类型 | 使用方式 | 原理 | 适用场景 |
---|---|---|---|
直接等待 | Thread.sleep(3000); | 强制线程等待 | 调试代码,临时性添加 |
隐式等待 | driver.manage(). timeouts().implicitlyWait( Duration.ofSeconds(10)); |
在时间范围内,轮询查找元素 | 解决找不到元素问题,无法解决交互问题 |
显式等待 | WebDriverWait(driver实例, 最长等待时间, 轮询时间).until(结束条件) |
设定特定的等待条件,轮询操作 | 解决特定条件下的等待问题,比如点击等交互性行为 |
那么在本章节我们学习了有关于自动化测试中,不同类型的等待机制针对不同的场景提供了有效的解决方案。直接等待通过 Thread点sleep; 实现强制线程休眠,虽然简单但是效率低下,通常用于调试代码或应对临时需求;隐式等待使用 driver点manage 点 timeouts 点 implicitlyWait Duration 点ofSeconds 10;,它在指定时间范围内以轮询方式查找目标元素,适用于解决元素未及时加载的问题,但无法确保元素处于可交互状态;显式等待通过 WebDriverWait driver 实例, 最长等待时间, 轮询时间 点 until 结束条件 实现针对特定条件的等待,比如等待元素可见或可点击,能够灵活地解决页面加载复杂或需要交互性操作的场景。三者各有优缺点,各有适用范围,在实际工作中还需要根据具体需求选择合适的等待方式,既保证脚本的可靠性,又提升其执行效率。