1、【隐式/显式等待】了解它们的工作机制,何时以及如何在测试中应用它们。
强制(直接)等待
强制等待,即让线程强制等待N秒钟,从而让元素达到就绪状态。
问题:页面加载渲染过程中,元素未就绪而抛出NoSuchElementException
解决方法:在元素定位报错代码前一行加上time.sleep(N)
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get("https://vip.ceshiren.com")
time.sleep(N)
el = driver.find_element(By.ID, "element_id") # 不加sleep,这一句查找元素语句抛出NoSuchElementException
注意
-
如果加了sleep能够解决问题,说明是元素未就绪
-
如果加了sleep无法解决问题,说明是别的问题导致,比如说定位器写错
-
强制等待只能用于脚本调试,提交到工程中的代码不能包含强制等待
缺点:不稳定(比如说,网速影响导致同一个元素在不同测试过程中加载时间不一致)
不确定页面加载时间,设置等待时间过长可能影响用例的执行效率,过短可能依然没等到元素就绪导致代码报错
解决方案:隐式等待
隐式等待
为元素等待设置一个灵活的全局等待时间N秒,N秒内能找到元素则继续后续的代码,N秒到了仍找不到元素则抛出NoSuchElementException
问题:难以确定元素加载就绪所需要的等待时间
解决方案:针对寻找元素的这个动作,使用隐式等待添加配置
特点:隐式等待全局生效,对所有find_element方法生效
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
# driver对象已创建,马上设置隐式等待
driver.implicitly_wait(N)
driver.get("https://vip.ceshiren.com")
el = driver.find_element(By.ID, "element_id")
缺点:只能解决元素查找的问题,不能解决元素无法交互的问题
原因
-
页面元素加载是异步加载过程,通常先加载html,再加载css、js
-
元素存在与否由HTML决定,元素的交互是由css或js决定的
-
隐式等待只关注元素能不能找到,不关注元素是否能够交互(点击、输入等)
解决方案:显式等待
显式等待
为元素交互设置一个灵活的总等待时间和一个轮询检测元素状态的时间间隔,脚本每隔一段时间就去检测一次元素是否达到指定的交互状态,如果达到了则退出等待,如未达到则进入下一次轮询间隔直到超出总等待时间。
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Edge()
driver.get("https://vip.ceshiren.com/#/ui_study")
WebDriverWait(driver, 10).until(expected_conditions.element_to_be_clickable((By.ID, "success_btn")))
driver.find_element(By.ID, "success_btn").click()
driver.quit()
当然,还会存在一些比较极端的场景,selenium自带的显示等待无法满足,则需要使用python装饰器/闭包结合WebDriverWait进行二次开发。
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.expected_conditions import WebDriverOrWebElement
from selenium.webdriver.support.wait import WebDriverWait
# 参照selenium源码进行二次开发
# 输入一:点击的目标按钮
# 输入二:期望出现的元素
def click_target_element_until_expected_element_to_be_visible(target_element, expected_element):
def _predicate(driver: WebDriverOrWebElement):
driver.find_element(*target_element).click()
return driver.find_element(*expected_element)
return _predicate
def wait_until():
driver = webdriver.Edge()
driver.get('https://vip.ceshiren.com/#/ui_study')
driver.maximize_window()
message_button_loc = (By.ID, 'primary_btn')
window_title_loc = (By.XPATH, '//span[text()="该弹框点击两次后才会弹出"]')
WebDriverWait(driver, 10).until(
click_target_element_until_expected_element_to_be_visible(
message_button_loc, window_title_loc
)
)
time.sleep(5)
driver.quit()
2、【Alert处理】理解不同类型的alert(如警告、确认、提示框)的处理方法。
Alert是指页面中无法通过定位器定位到的对话框。
selenium通过switch_to可切换到alert上进行操作。
# 点击alert确定
driver.switch_to.alert.accept()
# 点击alert忽略
driver.switch_to.alert.dismiss()
# 向alert输入框输入内容(较少见)
driver.switch_to.alert.send_keys("xxx")
alert本身处理逻辑不复杂,但alert出现会阻塞用例脚本后续操作,需要予以重视。
3、【Frame切换】学习在嵌套frames或iframes中定位元素和切换上下文。
【方法1】先定位iframe元素,再切换iframe
iframe = driver.find_element(By.ID, 'iframe_2')
driver.switch_to.frame(iframe)
【方法2】直接通过索引切换iframe,注意,索引从0开始
driver.switch_to.frame(0)
【还原】如果切换iframe后需要回退到默认body中,操作如下
driver.switch_to.default_content()
4、【多窗口切换】掌握如何处理和在多个标签页或窗口间切换。
个人心得:虽然通过driver.window_handles可以拿到每个标签页的句柄,但是句柄判断是哪个窗口比较麻烦,所以最好有新标签页弹出的时候马上切换过去,减少不必要的麻烦。
windows = self.driver.window_handles
self.driver.switch_to.window(windows[-1]) # 切换到最新窗口中
面试题
1. 解释隐式等待与显式等待的区别,并给出使用场景。
【回答思路】3种等待方式各自用在什么场景,缺点是什么,推导出下一种等待方式。
(答案见上文笔记)
2. 网页弹出alert框时,如何使用Selenium处理它?
(答案见上文笔记)
3. 如果一个页面中有多个frame,如何切换到第二个frame进行操作?
【方法1】先定位到第二个iframe,再切换
iframe = driver.find_element(By.ID, 'iframe_2')
driver.switch_to.frame(iframe)
【方法2】直接通过索引切换iframe,注意,索引从0开始
driver.switch_to.frame(1)
4. 打开新的浏览器窗口或标签页时,如何使用Selenium切换到新窗口或标签页?
(答案见上文笔记)
5. 在Selenium中如何获取元素的CSS属性,如颜色、字体大小等?
(待学习整理…)