Selenium自动化测试实战:从环境搭建到复杂场景处理
1. 项目概述:为什么Selenium依然是自动化测试的基石
如果你是一名测试工程师、开发人员,或者正在学习软件测试,那么“Selenium”这个名字你一定不陌生。它几乎是Web自动化测试的代名词,尽管近年来涌现了像Playwright、Cypress这样的新秀,但Selenium凭借其开源、跨浏览器、支持多语言的特性,依然牢牢占据着企业级自动化测试的核心地位。我接触Selenium超过八年,从最初的录制回放,到后来搭建复杂的分布式测试框架,它始终是项目中最可靠的那一环。今天,我们不谈那些高深莫测的框架设计,就从一个实战者的角度,拆解Selenium最核心、最基础的使用方法。无论你是想快速上手写几个脚本,还是为面试做准备,理解这些基础,远比盲目追求新工具更重要。
简单来说,Selenium是一个用于Web应用程序自动化测试的工具集。它的核心价值在于模拟真实用户的操作,比如点击按钮、输入文本、下拉选择等,从而解放人力,实现回归测试的自动化。它支持Java、Python、C#、JavaScript等多种主流编程语言,这意味着你的开发团队可以用最熟悉的语言来编写测试脚本。更重要的是,它通过WebDriver协议与各种浏览器(Chrome、Firefox、Edge、Safari等)直接通信,实现了对浏览器最底层的控制,这使得它的模拟行为非常真实和可靠。接下来,我将带你从环境搭建开始,一步步深入到元素定位、常见操作、等待机制等核心环节,并分享我这些年积累的实操心得和避坑指南。
2. 环境搭建与核心组件解析
2.1 语言与驱动选择:找到你的最佳拍档
开始使用Selenium的第一步,不是急着写代码,而是搭建一个稳定可用的环境。这里主要有三个核心组件需要准备:编程语言及Selenium库、浏览器、以及对应的浏览器驱动(WebDriver)。
编程语言选择
:Selenium支持多语言,但Python和Java是社区最活跃、资源最丰富的两种。对于测试入门和快速开发,我强烈推荐Python。它的语法简洁,库生态丰富(如
pytest
用于测试组织),能让你更专注于自动化逻辑本身,而非语言细节。对于大型企业级项目,特别是与Java技术栈深度集成的环境,Java则是更稳妥的选择。本文将以Python为例进行演示,因为其上手速度最快,也最能体现Selenium的易用性。
安装Selenium库
:使用Python的包管理工具pip,一行命令即可完成安装。这里有一个关键细节:建议指定一个相对稳定的版本,而不是永远使用最新的
selenium
。因为最新版可能会引入未预见的兼容性问题。
pip install selenium==4.15.0
浏览器与WebDriver :Selenium通过WebDriver来控制浏览器。你需要为你要测试的浏览器下载对应的驱动程序。
- Chrome/Edge :下载ChromeDriver。这里有个非常重要的技巧: 驱动版本必须与你的浏览器大版本号一致 。你可以在Chrome浏览器的“设置”->“关于Chrome”中查看版本号,然后去ChromeDriver官网或国内镜像站下载对应版本。
- Firefox :下载geckodriver。
-
下载后,将驱动文件(如
chromedriver.exe)放在一个目录下,并将该目录路径添加到系统的环境变量PATH中。这是最规范的做法。当然,你也可以在代码中指定驱动的绝对路径,但这不利于脚本的跨环境移植。
注意:很多新手遇到的第一个“坑”就是驱动版本不匹配导致的报错,错误信息通常是“This version of ChromeDriver only supports Chrome version XXX”。务必确保版本对应。
2.2 你的第一个自动化脚本:从“Hello World”开始
环境就绪后,我们来写一个最简单的脚本,打开百度首页并搜索一个关键词。这个脚本将串联起Selenium最基础的几个操作。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time
# 1. 创建WebDriver实例,启动Chrome浏览器
# 如果chromedriver已在PATH中,可以直接这样写
driver = webdriver.Chrome()
# 如果未在PATH中,则需要指定路径
# driver = webdriver.Chrome(executable_path=r‘C:\path\to\chromedriver.exe’)
try:
# 2. 打开目标网址
driver.get(“https://www.baidu.com”)
# 等待一下,确保页面加载完成(初级做法,后面会讲更好的方式)
time.sleep(2)
# 3. 定位搜索框,并输入内容
# 通过元素的‘id’属性定位,这是最高效的方式之一
search_box = driver.find_element(By.ID, “kw”)
search_box.send_keys(“Selenium自动化测试”) # 输入文本
search_box.send_keys(Keys.RETURN) # 模拟键盘回车键进行搜索
# 4. 等待搜索结果加载
time.sleep(3)
# 5. 验证结果(简单示例:打印页面标题)
print(“当前页面标题是:”, driver.title)
# 可以进一步定位搜索结果元素进行断言,这里先省略
finally:
# 6. 关闭浏览器
# 使用quit()而不是close(),quit会关闭整个浏览器进程和驱动,更干净
driver.quit()
运行这段代码,你会看到Chrome浏览器自动打开,访问百度,输入关键词并搜索,最后在控制台打印出标题后关闭。这就是自动化测试的魔力开端。这里有几个关键点需要理解:
-
webdriver.Chrome():这个调用会启动一个全新的、干净的浏览器实例(通常是无头模式,但默认是有界面的)。它不同于你手动打开的浏览器。 -
find_element(By.ID, “kw”):这是Selenium最核心的功能之一—— 元素定位 。我们必须先告诉Selenium要操作页面上的哪个元素。By.ID表示使用HTML元素的id属性来定位,“kw”就是百度搜索框的id值。 -
send_keys():用于向输入框等元素模拟键盘输入。 -
driver.quit(): 务必在脚本最后调用 ,以释放浏览器进程和驱动资源。我习惯用try...finally结构确保即使脚本中途出错,浏览器也能被正确关闭,避免残留进程占用内存。
3. 核心技能一:八种元素定位策略详解
元素定位是Selenium自动化脚本的基石,找不到元素,一切操作都无从谈起。Selenium提供了8种主要的定位策略,我将它们分为 高优先级 和 备用方案 两类。
3.1 高优先级定位策略(首选)
1. ID定位 (
By.ID
)
:通过HTML元素的
id
属性定位。
id
在理想情况下应该是页面内唯一的,因此定位速度最快、最准确。
如果元素有稳定且唯一的id,请无条件首选它。
element = driver.find_element(By.ID, “username”)
2. CSS选择器定位 (
By.CSS_SELECTOR
)
:这是功能最强大、最灵活的定位方式,也是我个人的最爱。它可以通过元素标签、类、属性、层级关系等进行组合定位。
-
通过类名:
find_element(By.CSS_SELECTOR, “.submit-btn”)(注意类名前的点) -
通过属性:
find_element(By.CSS_SELECTOR, “input[name=‘email’]”) -
通过层级:
find_element(By.CSS_SELECTOR, “#form1 .input-group > input”)CSS选择器语法丰富,能应对绝大多数复杂场景,且在现代浏览器中解析速度非常快。
3. XPath定位 (
By.XPATH
)
:与CSS选择器类似强大,通过XML路径语言来定位元素。它在处理没有明显特征(如id、class)的元素时非常有用,可以通过文本内容、索引位置等定位。
-
绝对路径(不推荐,易变):
/html/body/div[1]/form/input[2] -
相对路径+属性:
//input[@placeholder=‘请输入手机号’] -
包含文本:
//button[contains(text(), ‘登录’)]
注意:XPath引擎遍历节点,在极端复杂的页面上可能比CSS选择器稍慢,且写出的表达式可能较长。但在处理动态ID或复杂结构时,它往往是“救命稻草”。
3.2 备用定位策略(酌情使用)
4. Name定位 (
By.NAME
)
:通过
name
属性定位。在表单元素中比较常见,但并非唯一。
5. Class Name定位 (
By.CLASS_NAME
)
:通过
class
属性定位。但一个元素的
class
往往有多个值(如
“btn btn-primary”
),且同类元素在页面中很多,容易定位到多个元素。
6. Tag Name定位 (
By.TAG_NAME
)
:通过标签名定位,如
“input”
,
“a”
。通常一个页面有大量相同标签,所以一般用于查找元素集合,或与其他定位方式结合。
7. 链接文本 (
By.LINK_TEXT
) / 部分链接文本 (
By.PARTIAL_LINK_TEXT
)
:专门用于定位超链接(
<a>
标签)。
LINK_TEXT
需要完全匹配链接文本,
PARTIAL_LINK_TEXT
可以匹配部分文本。
# 精确匹配
driver.find_element(By.LINK_TEXT, “忘记密码?”)
# 模糊匹配
driver.find_element(By.PARTIAL_LINK_TEXT, “密码”)
8. 定位多个元素 (
find_elements
)
:以上所有方法都有一个对应的复数形式
find_elements
(注意末尾的
s
)。它返回一个匹配到的元素列表(WebElement列表),如果没找到则返回空列表,而不会抛出异常。这在处理表格、列表项或不确定数量的元素时非常有用。
all_buttons = driver.find_elements(By.CSS_SELECTOR, “button.btn”)
print(f“找到了 {len(all_buttons)} 个按钮”)
for btn in all_buttons:
print(btn.text)
实操心得 :
- 定位策略优先级 :ID > CSS Selector > XPath > 其他。优先使用有语义化的ID和CSS选择器。
- 如何获取定位表达式 :不要手写!利用浏览器的开发者工具(F12)。在元素上右键 -> “检查”,然后在元素代码上右键,可以选择“Copy” -> “Copy selector”(CSS)或“Copy XPath”。但 不要完全依赖复制的值 ,它们可能生成过长或脆弱的表达式(尤其是绝对路径的XPath)。你应该基于复制的表达式进行简化优化。
-
验证定位器
:在开发者工具的Console面板中,可以使用
$$(“你的CSS选择器”)或$x(“你的XPath”)来快速测试定位表达式是否能找到元素。
4. 核心技能二:浏览器操作与元素交互
找到元素后,下一步就是与之交互。Selenium提供了丰富的API来模拟用户的所有操作。
4.1 基础元素操作
点击与输入 :这是最常用的操作。
# 点击元素
login_button = driver.find_element(By.ID, “login-btn”)
login_button.click()
# 清空输入框后输入
search_input = driver.find_element(By.NAME, “q”)
search_input.clear() # 先清空已有内容
search_input.send_keys(“新的搜索词”)
# 模拟特殊键,如回车、Tab、Ctrl+A等
from selenium.webdriver.common.keys import Keys
search_input.send_keys(Keys.ENTER)
# 组合键,如全选 (Ctrl+A)
search_input.send_keys(Keys.CONTROL, ‘a’)
获取元素状态与信息 :在自动化测试中,我们经常需要获取元素的信息来进行断言(验证)。
element = driver.find_element(By.ID, “some-element”)
# 获取文本内容(对于可见文本)
text = element.text
print(text)
# 获取属性值
attr_value = element.get_attribute(“href”) # 获取链接地址
attr_value = element.get_attribute(“value”) # 获取输入框的值
attr_value = element.get_attribute(“class”) # 获取类名
# 判断元素是否可见、是否可用、是否被选中(针对复选框/单选框)
is_displayed = element.is_displayed()
is_enabled = element.is_enabled()
is_selected = element.is_selected() # 用于 checkbox 或 radio
# 获取元素的CSS属性值
color = element.value_of_css_property(“color”)
4.2 浏览器窗口与导航操作
自动化脚本需要控制浏览器本身。
# 导航
driver.get(“https://www.example.com”) # 打开新页面
driver.back() # 后退
driver.forward() # 前进
driver.refresh() # 刷新
# 窗口操作
current_window = driver.current_window_handle # 获取当前窗口句柄
all_windows = driver.window_handles # 获取所有窗口句柄
driver.switch_to.window(all_windows[-1]) # 切换到最新打开的窗口
# 弹窗处理 (Alert/Confirm/Prompt)
alert = driver.switch_to.alert # 切换到弹窗
print(alert.text) # 获取弹窗文本
alert.accept() # 点击“确定”
# alert.dismiss() # 点击“取消”
# alert.send_keys(“输入内容”) # 向Prompt弹窗输入
# 框架/iframe切换
driver.switch_to.frame(“frame_name_or_id”) # 通过name或id切换
driver.switch_to.frame(driver.find_element(By.TAG_NAME, “iframe”)) # 通过元素切换
driver.switch_to.default_content() # 切回主文档
driver.switch_to.parent_frame() # 切回父框架
4.3 鼠标与键盘高级操作
对于更复杂的交互,如悬停、拖拽、右键菜单等,需要用到
ActionChains
类。
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
element = driver.find_element(By.ID, “menu”)
target = driver.find_element(By.ID, “target”)
actions = ActionChains(driver)
# 鼠标悬停
actions.move_to_element(element).perform()
# 拖放元素
actions.drag_and_drop(element, target).perform()
# 右键点击
actions.context_click(element).perform()
# 双击
actions.double_click(element).perform()
# 复杂的键盘鼠标组合操作(例如,按住Shift键点击)
actions.key_down(Keys.SHIFT).click(element).key_up(Keys.SHIFT).perform()
ActionChains
的原理是将一系列操作存储在一个队列中,调用
perform()
时才会按顺序执行。这允许你构建非常复杂的用户交互序列。
5. 核心技能三:三种等待机制的艺术
自动化测试中最常见也最令人头疼的问题之一就是“元素找不到”或“元素状态不对”。这十有八九是因为 页面尚未加载完成或元素尚未变得可交互 。Selenium提供了三种等待机制来解决这个问题,理解并正确使用它们是编写稳定脚本的关键。
5.1 强制等待 (
time.sleep
):不得已而为之
这是最原始的方法,让脚本无条件暂停指定的秒数。
import time
time.sleep(5) # 强制等待5秒
缺点 :效率极低。无论页面是否已就绪,都必须等待固定时间。如果网络慢,5秒可能不够;如果网络快,又会白白浪费时间。 仅在调试脚本或处理极特殊、无法用其他等待方式解决的场景时使用。
5.2 隐式等待 (
implicitly_wait
):设置全局超时
隐式等待告诉WebDriver:在查找任何一个元素时,如果元素没有立即出现,就等待一段固定的时间,直到元素出现或超时。
driver.implicitly_wait(10) # 单位为秒
设置后,对后续所有的
find_element
和
find_elements
调用都生效。如果在10秒内找到元素,则立即返回;如果超时仍未找到,则抛出
NoSuchElementException
。
优点
:设置一次,全局生效,代码简洁。
缺点
:
- 它只对“查找元素”这一操作有效,对元素的“可点击”、“可见”等状态无效。
- 它是全局设置,可能会在某些不需要等待的地方产生不必要的延迟。
- 与显式等待混用时可能导致意想不到的超时行为(总等待时间可能是两者之和)。
5.3 显式等待 (
WebDriverWait
):推荐的最佳实践
显式等待是针对某个特定条件进行等待,条件满足则立即继续,超时则抛出异常。它提供了最大的灵活性和精确控制。这是我最推荐的方式。
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
# 创建一个WebDriverWait对象,设置最大等待时间10秒,轮询间隔0.5秒(默认)
wait = WebDriverWait(driver, 10)
# 等待元素出现在DOM中并且可见、可点击
element = wait.until(EC.element_to_be_clickable((By.ID, “submit-button”)))
element.click()
# 等待元素可见
element = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, “.result”)))
# 等待元素包含特定文本
element = wait.until(EC.text_to_be_present_in_element((By.ID, “status”), “成功”))
# 等待页面标题包含特定文字
wait.until(EC.title_contains(“订单详情”))
# 等待旧元素消失(例如,等待加载动画消失)
wait.until(EC.invisibility_of_element_located((By.ID, “loading-spinner”)))
expected_conditions
(EC) 模块提供了大量预定义的条件,如
presence_of_element_located
(元素存在于DOM)、
visibility_of_element_located
(元素可见)、
element_to_be_clickable
(元素可点击)等。
优点
:
- 精准 :只为必要的条件等待。
- 灵活 :可以自定义等待条件。
- 清晰 :代码明确表达了在等待什么。
我的等待策略建议 :
- 完全避免使用强制等待 在生产脚本中。
- 可以设置一个较短的全局隐式等待 (如3-5秒),作为基础保障。
- 对于所有关键操作 ,特别是点击、输入前的元素状态确认, 优先使用显式等待 。这是编写健壮、稳定自动化脚本的核心技巧。
6. 实战进阶:处理常见复杂场景
掌握了基础操作和等待机制,你已经可以应对大部分简单页面的自动化了。但在实际项目中,你会遇到各种“拦路虎”。下面分享几个高频复杂场景的处理方案。
6.1 处理动态加载与Ajax内容
现代网页大量使用Ajax技术异步加载内容。一个典型的例子是:点击“加载更多”按钮,新的内容项会动态添加到页面底部,而页面不会刷新。 挑战 :你无法直接定位尚未出现在DOM中的元素。 解决方案 :结合显式等待和查找多个元素。
# 假设每次点击“加载更多”会加载10条新评论
load_more_button = driver.find_element(By.ID, “load-more”)
initial_comments = driver.find_elements(By.CLASS_NAME, “comment-item”)
print(f“初始有 {len(initial_comments)} 条评论”)
# 点击加载更多
load_more_button.click()
# 等待新的评论元素出现。我们等待评论数量增加。
def comments_increased(driver):
current_comments = driver.find_elements(By.CLASS_NAME, “comment-item”)
# 如果当前评论数大于之前的数量,条件满足
return len(current_comments) > len(initial_comments)
wait = WebDriverWait(driver, 10)
wait.until(comments_increased)
final_comments = driver.find_elements(By.CLASS_NAME, “comment-item”)
print(f“加载后共有 {len(final_comments)} 条评论”)
这里的关键是自定义了一个等待条件
comments_increased
,它检查元素列表的长度是否发生了变化。这是一种非常实用的模式。
6.2 处理下拉选择框 (
<select>
)
对于原生的HTML
<select>
元素,Selenium提供了专门的
Select
类,比模拟点击选项更稳定。
from selenium.webdriver.support.ui import Select
# 定位到<select>元素
select_element = driver.find_element(By.ID, “country-select”)
# 创建Select对象
select = Select(select_element)
# 选择方式有三种:
select.select_by_value(“us”) # 通过option的value属性值选择
select.select_by_visible_text(“美国”) # 通过选项的可见文本选择
select.select_by_index(1) # 通过索引选择(从0开始)
# 获取所有已选选项
all_selected_options = select.all_selected_options
# 获取所有选项
all_options = select.options
# 判断是否是多选
is_multiple = select.is_multiple
对于非原生(用div/ul/li模拟的)精美下拉框,则无法使用
Select
类,需要模拟点击展开,再点击目标选项。
6.3 文件上传
文件上传通常有两种形式:
-
Input类型为file的元素 :这是最简单的,直接使用
send_keys传入文件路径即可。upload_input = driver.find_element(By.CSS_SELECTOR, “input[type=‘file’]”) # 传入文件的绝对路径 upload_input.send_keys(“/Users/yourname/Desktop/test_image.jpg”)注意:路径必须是绝对路径,且脚本运行环境(可能是远程服务器)必须有该文件的访问权限。
-
通过点击按钮触发的系统文件选择对话框 :Selenium 无法直接操作 系统级别的对话框。这是Selenium的一个限制。常见的解决方案有:
-
规避
:如果可能,让开发同学在测试环境提供一个直接通过
input[type=‘file’]上传的入口。 - 使用AutoIT、Robot类等第三方工具 (不跨平台,且复杂)。
-
使用浏览器开发者工具
:在测试时,可以手动操作一次,观察网络请求,然后尝试在自动化脚本中直接模拟这个HTTP POST请求(使用
requests库),绕过前端界面。这需要一定的前后端联调知识。
-
规避
:如果可能,让开发同学在测试环境提供一个直接通过
6.4 截图与日志记录
自动化脚本运行,尤其是无人值守时,截图是排查问题最直观的证据。
from datetime import datetime
# 截取整个浏览器窗口
timestamp = datetime.now().strftime(“%Y%m%d_%H%M%S”)
screenshot_path = f“./screenshot_error_{timestamp}.png”
driver.save_screenshot(screenshot_path)
print(f“截图已保存至:{screenshot_path}”)
# 截取特定元素
element = driver.find_element(By.ID, “error-message”)
element.screenshot(‘./element_screenshot.png’)
良好的日志记录习惯也至关重要。建议使用Python内置的
logging
模块,为脚本添加不同级别的日志输出(INFO, DEBUG, ERROR),便于追踪执行流程和定位问题。
7. 常见问题排查与脚本优化技巧
即使按照最佳实践编写脚本,依然会遇到各种问题。这里记录了我踩过的一些“坑”和解决方法。
7.1 元素定位失败问题排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
NoSuchElementException
(元素不存在)
|
1. 定位器写错了。
2. 页面尚未加载完成。 3. 元素在iframe/frame内。 4. 元素是动态生成的(Ajax)。 5. 页面有多个匹配元素,
find_element
只返回第一个但可能不是你想要的。
|
1. 在浏览器控制台用
$$()
或
$x()
验证定位器。
2. 添加显式等待 ,等待元素出现。 3. 使用
driver.switch_to.frame()
切换到正确的frame。
4. 等待动态内容加载完成(见6.1)。 5. 使用
find_elements
检查匹配数量,优化定位器使其更精确。
|
ElementNotInteractableException
(元素不可交互)
|
1. 元素不可见(被遮挡、CSS隐藏)。
2. 元素未处于可操作状态(如disabled)。 3. 有弹窗、蒙层覆盖。 |
1. 使用
EC.visibility_of...
或
EC.element_to_be_clickable
等待。
2. 检查元素
disabled
属性。
3. 关闭或处理覆盖层。 |
StaleElementReferenceException
(元素过时引用)
|
1. 之前找到的元素,在操作前页面已经刷新或该部分DOM被重新渲染。
2. 常见于单页应用(SPA)。 | 这是高级问题 。解决方案是 重新查找元素 。最好在每次操作前即时定位,或使用“Page Object模式”的懒加载方式定位元素。 |
| 脚本在本地运行成功,在服务器/CI上失败 |
1. 浏览器/驱动版本不一致。
2. 服务器无图形界面(headless模式)。 3. 屏幕分辨率、字体渲染差异导致元素位置偏移。 4. 网络环境差异导致加载超时。 |
1. 统一环境版本,使用Docker容器化。
2. 确保Headless模式配置正确(添加
--headless
参数)。
3. 设置固定的浏览器窗口大小
driver.set_window_size(1920, 1080)
。
4. 增加全局等待时间,优化网络超时设置。 |
7.2 提升脚本稳定性和可维护性
1. 使用Page Object模式 (PO模式) :这是UI自动化测试的经典设计模式。其核心思想是将页面封装成类,页面的元素定位和操作作为类的方法。测试脚本只调用这些方法,不直接包含定位器。这样做的好处是:
- 可维护性高 :页面UI变动时,只需修改对应的Page类,无需修改大量测试脚本。
- 可读性强 :业务逻辑清晰。
- 减少重复代码 :公共操作可以封装在基类中。
一个简单的Page Object示例:
# login_page.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginPage:
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# 定位器
USERNAME_INPUT = (By.ID, “username”)
PASSWORD_INPUT = (By.ID, “password”)
LOGIN_BUTTON = (By.ID, “login-btn”)
ERROR_MSG = (By.CLASS_NAME, “error-message”)
# 页面操作方法
def enter_username(self, username):
element = self.wait.until(EC.visibility_of_element_located(self.USERNAME_INPUT))
element.clear()
element.send_keys(username)
def enter_password(self, password):
element = self.wait.until(EC.visibility_of_element_located(self.PASSWORD_INPUT))
element.clear()
element.send_keys(password)
def click_login(self):
element = self.wait.until(EC.element_to_be_clickable(self.LOGIN_BUTTON))
element.click()
def get_error_message(self):
try:
element = self.wait.until(EC.visibility_of_element_located(self.ERROR_MSG))
return element.text
except:
return None
# 在测试脚本中使用
def test_login_failure(driver):
login_page = LoginPage(driver)
driver.get(“https://example.com/login”)
login_page.enter_username(“wrong_user”)
login_page.enter_password(“wrong_pass”)
login_page.click_login()
assert “用户名或密码错误” in login_page.get_error_message()
2. 配置浏览器选项,应对反爬与检测
:一些网站会检测Selenium的自动化特征(如
window.navigator.webdriver
属性)。虽然Selenium主要用于测试,但了解如何“隐藏”特征有时对测试某些功能也有必要。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
# 常用选项
options.add_argument(“--disable-blink-features=AutomationControlled”) # 禁用自动化控制特征
options.add_argument(“--start-maximized”) # 启动时最大化窗口
options.add_argument(“--incognito”) # 无痕模式
# 添加实验性选项,排除收集的启用自动化开关
options.add_experimental_option(“excludeSwitches”, [“enable-automation”])
options.add_experimental_option(‘useAutomationExtension’, False)
# 创建驱动时传入选项
driver = webdriver.Chrome(options=options)
# 执行CDP命令,覆盖navigator.webdriver属性(仅Chrome)
driver.execute_cdp_cmd(“Page.addScriptToEvaluateOnNewDocument”, {
“source”: “””
Object.defineProperty(navigator, ‘webdriver’, {
get: () => undefined
});
“””
})
请注意,这些方法可能随着浏览器更新而失效,且用于爬虫可能涉及法律和道德问题,但在测试环境中用于绕过不必要的检测是常见做法。
3. 无头模式运行 :在服务器或CI/CD流水线中,通常没有图形界面。此时需要在无头模式下运行浏览器,节省资源。
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument(“--headless=new”) # Chrome较新版本的无头模式参数
options.add_argument(“--no-sandbox”) # Linux环境下常需要
options.add_argument(“--disable-dev-shm-usage”) # 解决共享内存问题
options.add_argument(“--disable-gpu”) # 某些系统需要
driver = webdriver.Chrome(options=options)
在无头模式下,所有操作都在后台进行,看不到浏览器界面,但截图功能依然可用,这对于调试至关重要。
掌握Selenium的基本使用,就像是学会了一套强大的“浏览器操控术”。从精准的元素定位到灵活的等待策略,从处理复杂交互到编写健壮的Page Object代码,每一步都凝结了实战中的经验与教训。我个人的体会是,自动化测试脚本的稳定性,30%取决于工具本身,70%取决于编写者对页面加载逻辑、异步处理和异常场景的理解与处理。不要满足于让脚本“跑起来”,而要追求让它“在任何时候都能稳定地跑起来”。这需要你像侦探一样,仔细观察页面行为,分析网络请求,并运用合理的等待和重试机制。最后,记住自动化测试的初衷是提升效率,而不是制造维护负担。从简单的核心流程开始,逐步构建你的测试用例库,并配以良好的框架设计和日志报告,才能真正让Selenium成为你项目质量保障的得力助手。
更多推荐
所有评论(0)