Selenium常用函数实战指南:从元素定位到文件上传全场景覆盖

发布时间:2026/10/12 5:31:50
Selenium常用函数实战指南:从元素定位到文件上传全场景覆盖 我们去面试自动化测试岗位的时候十次有八次会被问到同一个问题Selenium 里你最常用的函数有哪些这个问题看着简单但回答得好的候选人其实不多。多数人只能说出 find_element 和 click、send_keys再往深里问——元素带放大镜图标框不住怎么办、页面弹窗不是 input 标签怎么传文件、窗口切换为什么总是时灵时不灵——就开始含糊其辞。说到底是平时写脚本只图“能跑”没把 Selenium 这套 API 的用法体系化梳理过。这篇东西我想换个写法不按文档顺序平铺而是按真实项目中遇到最多的高频场景把 Selenium 常用函数拆开揉碎讲一遍。从八种元素定位的取舍、等待机制的底层逻辑到文件上传的三种处理姿势、iframe 和窗口切换的坑再到 JS 执行、键盘鼠标操作、数据断言这些实战必备技巧。目标就一个你把这篇文章看完、练完再遇到界面自动化的需求手上能直接抄的解决方案至少覆盖九成场景。这一系列文章前面几篇聊过测试理论、框架设计这篇属于实战工具篇适合正在学自动化测试的初级工程师、准备面试的求职者以及想把自己的脚本写得更稳的进阶选手。内容会尽量口语化中间的代码示例都用 Python 版本Selenium 4.x 为主部分地方会提到和旧版 3.x 的差异。1. 元素定位自动化脚本的地基先把它打牢Selenium 的所有操作都建立在“找到元素”这个动作之上。你写得再多技巧定位这一步失败后面全部白搭。所以我把这部分放在开头而且会多花点篇幅去讲定位函数的选用逻辑——不是背八种定位方式的名字而是搞懂每种方式适合什么场景、优先级怎么排、XPath 怎么写才不会“一天一小挂、三天一大挂”。1.1 八种定位方式实战中的优先级排序Selenium 官方提供了 find_element 和 find_elements 两个基础方法配合 By 枚举传入不同的定位策略。Python 代码长这样from selenium import webdriver from selenium.webdriver.common.by import By driver webdriver.Chrome() element driver.find_element(By.ID, username) elements driver.find_elements(By.CSS_SELECTOR, .list-item)By 下面支持八种定位方式ID、Name、Class Name、Tag Name、Link Text、Partial Link Text、XPath、CSS Selector。但实际项目里没有人会把这八种都用一遍真正高频的其实就四种ID、CSS Selector、XPath外加少量情况下的 Link Text。我给一个自己在团队里定的优先级参考优先级定位方式适用场景注意点第一选择ID登录框、搜索框、表单控件这类固定交互元素有些前端框架会动态生成 id需要先看源码确认第二选择CSS Selector结构相对稳定的页面元素、组合条件定位语法简洁Selenium 执行效率高于 XPath第三选择XPath无法用 id/class 唯一定位、需要根据文本内容定位灵活性强但写不好会性能差、稳定性差补充方案Link Text导航链接、按钮文字恰好是链接文本只能用于 a 标签偶尔用Name/Class/Tag批量元素统计、低复杂度页面单独使用时命中率很低不建议独立依赖这个排法不是拍脑袋。ID 在 HTML 标准里就要求文档唯一命中率天然有保证CSS Selector 在浏览器的 document.querySelector 层面有原生优化运行效率比 XPath 高XPath 虽然慢一点但它是唯一能按“元素文本内容”定位的方式在某些场景里绕不开。Name 和 Class 的问题在于前端工程师经常会复用样式名.btn、.input一个 class 下面挂十几个元素是家常便饭单靠它定位等于听天由命。1.2 XPath 和 CSS 的写法进阶不是越长越好初学者最爱干一件事打开浏览器的开发者工具右键复制 XPath直接把那一长串 //*[idapp]/div[3]/div/div[2]/form/div[1] 塞进脚本里。这种写法在页面刷新后大概率失效因为加了点前端展示逻辑dom 结构一变就跪。写 XPath 的核心原则是“用属性少用层级”。举个例子要定位登录页面的用户名输入框源码长这样div classform-group label forusername用户名/label input typetext idusername nameuser classform-control / /div依次评估几种写法# 脆弱写法三层节点依赖改一个类名就挂 driver.find_element(By.XPATH, /html/body/div[1]/div[2]/form/div[1]/input) # 相对好一点但如果页面上还有其他 id 包含 user 的节点仍然有风险 driver.find_element(By.XPATH, //input[contains(id, user)]) # 最稳妥的写法直接锁定目标元素的必填属性 driver.find_element(By.XPATH, //input[idusername]) # 同样效果边界更明确 driver.find_element(By.CSS_SELECTOR, input#username)另一个常见需求是“找某个文本对应的元素”比如点击列表里文字为“删除”的按钮XPath 可以这样处理driver.find_element(By.XPATH, //button[text()删除]) driver.find_element(By.XPATH, //span[contains(text(), 确认删除)])contains(text(), ...) 写法有个需要注意的细节如果目标元素的文本被拆到了多个子节点里text() 取的是当前节点的直接文本可能为空。这时候更稳的方案是用 .// 配合 normalize-space()driver.find_element(By.XPATH, //div[normalize-space(.)保存并继续])CSS 这边的相对定位也可以玩出花比如按属性前缀、后缀匹配/* 匹配 name 以 login 开头的元素 */ [name^login] /* 匹配 class 以 btn 结尾的元素 */ [class$btn] /* 匹配 href 里包含 product 的元素 */ a[href*product]这些是必须掌握的。定位这东西像交朋友目标越清晰关系越稳定——你要用“全名唯一工号”去找人而不是用“住在左边的那个穿红衣服的同事”这种描述。1.3 定位不到元素先查这五个原因遇到 NoSuchElementException先别急着改定位表达式。我每次排查都按下面这个顺序过一遍命中率超过八成第一元素是否在 iframe 里。这是最高频的坑。页面里嵌了 iframe 但脚本没有切进去你再怎么写 XPath 都找不到。第二元素是否在 Shadow DOM 里。前端组件库比如某些自定义日历、富文本会把内部元素包在 shadow-root 里普通 find_element 是拿不到的。后面专门讲处理方案。第三页面是否还没渲染完。不是页面 load 完了元素就在很多 SPA 站点是异步接口返回后才渲染表单需要等某个元素出现再操作。第四元素是否被遮挡。有可能是弹层挡住有可能是透明度遮罩覆盖Selenium 可以找到元素但点击时会报 ElementClickInterceptedException。第五页面存在多份相同元素比如两个窗口、两个 tab只关闭了当前句柄但旧句柄还活着定位出来的不是你想操作的那个。简单排查法先打印 driver.page_source 里的片段或者确认当前窗口句柄再决定下一步。2. 高频交互函数从输入点击到文件上传的完整弹药库定位只是第一公里。拿到元素之后要做的所有事情——输入文字、点击、清空、选择下拉框、拖拽、上传文件——都依赖 Selenium 提供的交互函数。这里我会按真实项目中的使用频率来排序把每个函数的应用场景、边界情况和容易踩的坑一起讲清楚。2.1 文本输入与点击看似简单细节不少最基础的三个方法send_keys() 输入文本click() 点击元素clear() 清空内容。示例如下driver.find_element(By.ID, username).send_keys(测试账号) driver.find_element(By.ID, username).clear() driver.find_element(By.ID, login-btn).click()用起来不复杂但有几个实际问题必须注意。第一个是 send_keys 的追加行为如果元素里已经有默认值市面上多数脚本的写法是直接 send_keys结果就变成了“原有内容新内容”。所以真正规范的流程是先 clear()再 send_keys()。有些前端组件的 clear() 不生效比如封装了 React 受控组件的输入框这时候可以用快捷键全选删除先 ctrla 选中再直接输入新内容覆盖。from selenium.webdriver.common.keys import Keys element driver.find_element(By.ID, search-input) element.send_keys(Keys.CONTROL, a) # 全选 element.send_keys(新的搜索词)第二个是 click() 的失效场景。按钮上方悬着浮动层、元素不可见但存在、页面在点击瞬间发生重绘这些都可能让 click() 不出效果。遇到的时候不要强行调用原生 click而是用 JavaScript 触发点击兜底代码在后面 JS 执行部分会给。第三个是文件上传类的 input 标签send_keys 直接传本地路径即可这个属于上传专门的高频场景我在第四部分单独展开。还有一个高频组合是表单提交。用户填完用户名密码你当然可以直接点登录按钮但更贴近真实用户行为的是按回车提交password driver.find_element(By.ID, password) password.send_keys(123456) password.send_keys(Keys.ENTER)回车提交有一个隐含好处能触发前端 form 的 submit 校验逻辑你点按钮反而可能绕过某些非必填校验导致测试场景失真。2.2 下拉框与多选控件select 对象的正确姿势网页里的下拉框分两类。一类是原生select标签另一类是 JavaScript 模拟的假下拉div 列表 点击事件。两者处理方式完全不同我分别讲。原生 select 标签用 Selenium 的 Select 类处理不仅代码干净而且语义清晰select idcity option valuebeijing北京/option option valueshanghai上海/option option valueguangzhou广州/option /selectfrom selenium.webdriver.support.ui import Select city_select Select(driver.find_element(By.ID, city)) city_select.select_by_value(shanghai) # 按 value 属性选 city_select.select_by_visible_text(北京) # 按可见文本选 city_select.select_by_index(2) # 按下标选从0开始select_by_visible_text 在中文页面上最直观我通常是首选。select_by_value 适合 value 属性有稳定业务含义的场景。select_by_index 排在最后因为一旦前端在中间插入一个选项脚本就得跟着改。另外一个常见操作是读取当前选中的选项、或者校验回显值selected city_select.first_selected_option print(selected.text) # 多选 select 用 all_selected_options multi_select Select(driver.find_element(By.ID, hobbies)) all_selected multi_select.all_selected_options遇到 JavaScript 模拟的下拉框Select 类型就失效了——本质是把下拉点击开等选项列表加载出来然后精准点目标项。这类组件的选项项通常在 ul/li 或者 div 的结构里用文本定位就特别顺手driver.find_element(By.CSS_SELECTOR, .select-trigger).click() driver.find_element(By.XPATH, //li[contains(text(), 上海)]).click()2.3 键盘与鼠标事件模拟更复杂的用户路径有时候测试用例要求的不是“点一下按钮”而是“双击单元格进入编辑”“右键呼出菜单”“拖拽滑块到指定位置”。Selenium 的 ActionChains 类就是干这个的。键盘组合键在前面已经用过一个 CtrlA这里补充几个常见场景。需要强调的坑send_keys 带组合键时Windows/Linux 用 Keys.CONTROLmacOS 上要改成 Keys.COMMAND。from selenium.webdriver.common.keys import Keys # 回车 / Tab 切换焦点 element.send_keys(Keys.TAB) # 复制粘贴 element.send_keys(Keys.CONTROL, c) target.send_keys(Keys.CONTROL, v) # 回退删除 element.send_keys(Keys.BACKSPACE)鼠标操作类场景我用得最多的是这几类悬停看下拉菜单、双击进入编辑状态、右键打开业务菜单、拖拽滑块做验证码或调节器。看一个鼠标悬停的例子from selenium.webdriver.common.action_chains import ActionChains user_menu driver.find_element(By.CSS_SELECTOR, .user-avatar) ActionChains(driver).move_to_element(user_menu).perform() dropdown_item driver.find_element(By.LINK_TEXT, 个人中心) dropdown_item.click()这里有个小坑move_to_element 只移动鼠标不触发点击展示菜单的逻辑一般是悬停后 CSS 控制的所以先悬停再定位菜单元素这是固定套路。拖拽操作使用 drag_and_drop。比如滑块验证码简化场景from selenium.webdriver.common.action_chains import ActionChains slider driver.find_element(By.CSS_SELECTOR, .slider-btn) target driver.find_element(By.CSS_SELECTOR, .slider-target) ActionChains(driver).drag_and_drop(slider, target).perform()如果你的拖拽目标是一个动态计算的距离可以换用 click_and_hold move_by_offset releaseActionChains(driver) \ .click_and_hold(slider) \ .move_by_offset(xoffset180, yoffset0) \ .pause(0.5) \ .release() \ .perform()move_by_offset 里 xoffset 的位移数值需要自己先算一般是缺口距离减去滑块宽度而这个距离可以通过截图和像素对比大致估出来自动化项目里常见做法是“滑过头了再回退几像素”。ActionChains 还有一个高频应用页面滚动到某个元素可见后再操作。普通场景用 driver.execute_script 直接滚但有的元素需要先悬停才能正确位置这时配合 scroll_to_element 也能解决问题——不过这个函数实操中表现不稳定我更倾向于直接 JS 滚这个在后面的 JS 执行部分细说。3. 等待机制脚本时灵时不灵的病根在这里治我见过太多脚本失败不是因为定位表达式错了而是页面元素没就绪脚本就急着去操作。Selenium 脚本跑不过夜十次里有七次都是同步问题。这一章把三种等待讲透后面你再遇到 Click 不生效、元素找不到这类问题思路会清晰很多。3.1 三种等待的对比什么时候用哪个Selenium 提供三种等待强制等待time.sleep、隐式等待implicitly_wait、显式等待WebDriverWait expected_conditions。先给结论强制等待能不用就不用隐式等待给个兜底显式等待是核心方案。强制等待就是写死 sleep(5)它的坏处不用多解释页面快的时候白白浪费5秒页面慢的时候5秒可能还不够。而且在机器负载高的时候time.sleep 的误差很大测试结果不稳定。隐式等待是全局性的只要设置一次它会在每次 find_element 的时候轮询等待元素出现默认 500ms 轮询一次。我一般这样设置driver.implicitly_wait(10)注意隐式等待只作用于元素查找不作用于元素可点击、可见、包含文本等状态判断。另外隐式等待配合显式等待使用时如果全局设了10秒显式等待也设了5秒实际最长可能跑到接近15秒才报错增加无谓的等待时间。所以很多团队干脆把隐式等待设短重逻辑全部交给显式等待。显式等待是对特定条件的精准等待代码可读性强、逻辑表达准确。下面的写法是真正贯穿全部自动化项目的核心模式from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 等元素可点击比如弹窗按钮 WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, confirm-btn)) ).click() # 等元素可见 WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, .toast-success)) ) # 等元素消失比如 loading 遮罩 WebDriverWait(driver, 10).until( EC.invisibility_of_element_located((By.CSS_SELECTOR, .spinner)) )显式等待配合的函数有很多presence_of_element_located元素存在于 DOM、visibility_of_element_located元素可见、element_to_be_clickable元素可见且可点击、text_to_be_present_in_element元素包含指定文本等等。这些都是 Selenium 在 expected_conditions 里内置好的常用条件比我拿到元素后自己轮询判断 else break 要稳定得多。3.2 显式等待的正确用法可别把等待函数本身写错新手最容易犯的错是把 element 实例传进 EC 条件而不是把定位元组传进去。看一个对比# 错误示范传了元素本身EC 条件每次判断时元素已过期就会抛异常 element driver.find_element(By.ID, username) WebDriverWait(driver, 10).until(EC.visibility_of(element)) # 正确示范传定位条件WebDriverWait 内部重新查找 WebDriverWait(driver, 10).until( EC.visibility_of_element_located((By.ID, username)) )element 和 locator 是两码事前者是某个时刻查到的一个对象引用后者是“如何重新找到这个元素”的描述。页面一刷新之前的 element 就 stale 了ElementNotInterruptableException 或者 StaleElementReferenceException但 locator 永远有效。还有个建议把显式等待封装成通用方法而不是在每一步都写一长串。自定义一个 wait_click、wait_input 之类的小工具函数使用体验会好一个量级。比如这样def wait_click(driver, by, value, timeout10): return ( WebDriverWait(driver, timeout) .until(EC.element_to_be_clickable((by, value))) .click() ) def wait_visible(driver, by, value, timeout10): return WebDriverWait(driver, timeout).until( EC.visibility_of_element_located((by, value)) )这样测试用例代码就变成了wait_click(driver, By.ID, login-btn) wait_visible(driver, By.CLASS_NAME, dashboard)可读性不是好一点半点而且统一处理了重试和时间开销。3.3 自定义预期条件内置条件不够用的时候怎么办业务场景千变万化内置的 EC 条件偶尔也覆盖不上。比如你要等到某个接口返回的特定文本出现在页面角落用 text_to_be_present_in_element 肯定不够因为那个文本根本不在任何元素里你需要自己写一个条件。自定义条件本质上是写一个函数入参是 driver返回值为真或假class page_has_text: def __init__(self, text): self.text text def __call__(self, driver): return self.text in driver.page_source WebDriverWait(driver, 10).until(page_has_text(订单已完成))等价写法用 lambda 也能搞定但类写法更清晰。实际项目里有个高频场景等待某元素具备某个 CSS 属性值比如按钮变灰到变亮这种也可以封装成自定义 expected_condition。总之WebDriverWait 的 until 内部只关心“函数返回是否为 True”这就是它灵活的内在机制。4. 文件上传三种主流方案的完整求解文件上传是自动化测试里被问得最多的高频场景之一也是很多人觉得“怎么搞都别扭”的重灾区。原因很现实上传控件的类型太多有的原生支持 file input有的需要打开系统文件选择框还有的是拖拽/粘贴上传。只背一个 send_keys 的教程根本不落地这里按三种情况分别给方案。4.1 方案一input 标签一行代码解决最长见的情况是网页里有一个input typefile。这是 Selenium 最容易处理的场景因为 Selenium 不允许也不应该操作系统级窗口但 file input 本身就是页面元素直接把本地文件路径传给 send_keys 就行upload_input driver.find_element(By.CSS_SELECTOR, input[typefile]) upload_input.send_keys(/Users/me/Downloads/test_report.pdf)如果要传多个文件input 标签配置了 multiple 属性路径用换行符分隔upload_input.send_keys(/path/to/a.pdf\n/path/to/b.pdf)这个方案的要点是定位 input 标签的时候不一定非看到那个“选择文件”按钮很多页面 input 是隐藏的display:none但这并不影响 send_keys。常规的 visibility 检查在这里不一定适用如果用了 EC.visibility_of_element_located 反而会失败。正确判断直接用 presence_of_element_located 或者 element_to_be_clickable。4.2 方案二非 input 标签先试试键击发送并不是所有系统都让你直接操作 input。部分内网系统、老旧的 Java 上传组件、人脸识别类控件或者是某些商业控件webuploader、layui 封装后的上传根本不是普通 input点上传按钮会唤起系统窗口Selenium 无法直接触达系统窗口按钮。处理思路有两条我按优先级排列。第一步先尝试一个反直觉但好用的方法点开上传组件后直接对已聚焦的元素发送路径。其实不管上传控件是不是 input 标签很多组件在弹窗打开前会生成一个隐藏的 file input 用于接收文件选择结果你只要能把路径送进去就行。做法是找页面上所有 input typefile 的节点直接用 send_keys 硬塞路径inputs driver.find_elements(By.CSS_SELECTOR, input[typefile]) for input_el in inputs: try: input_el.send_keys(C:/tmp/upload_file.txt) break except Exception: continue这个方法能解决相当一部分假上传框的兼容问题。不要小看它我帮同事排查上传用例时十次里拿这个方案解决了至少一半。4.3 方案三Windows 弹窗兜底用外部工具接管如果页面连隐藏 input 都没有上传是纯 Flash 或纯 JS 组件点击后弹出的是系统的“文件选择”对话框那就绕不开系统级操作了。最常用的方案是 Python 的 pywinautoWindows或者 pyautogui 直接模拟键盘输入路径。这里给一个在 Windows 下用 pywinauto 的标准流程# 点击上传按钮触发系统对话框 driver.find_element(By.ID, upload-btn).click() # 切换并控制系统窗口 from pywinauto import Desktop # 等待对话框出现后往文件名输入框里输入路径再点打开 try: dlg Desktop(backenduia).window(title打开) dlg.wait(visible, timeout10) dlg.Edit.set_text(D:\\reports\\upload.xlsx) dlg.Button.click() except Exception: # 回退方案直接 pyautogui 输入 import pyautogui pyautogui.sleep(1) pyautogui.typewrite(D:\\reports\\upload.xlsx, interval0.05) pyautogui.press(enter)需要特别提醒pyautogui 是全局键盘鼠标模拟执行时不要动鼠标键盘否则会串场。这个方案我通常放在最后因为它对执行环境要求高、可维护性差脚本换台机器可能就因为输入法或分辨率不一样而失败。有条件的话优先引导前端改造上传组件或者走接口层测试来覆盖文件上传场景。还有一个纯前端的技巧拖拽上传场景可以尝试直接用 JS 构造 DataTransfer 对象把 File 放进输入框的 files 属性。这个方案对前端容器要求较高我见过有人用得很溜但从零写起来代码量大而且未来维护成本不小这里就不展开了。5. 窗口、iframe 与 JS 执行解决顽固问题的三把钥匙前四章覆盖了“找元素-做操作-等就绪-传文件”的主链路。但实际项目中Selenium 脚本的大量翻车现场发生在两个特殊容器里多窗口和多层框架。外加一个 JavaScript 执行能力它可以解决一些光靠 UI 操作解决不了的问题。我把这三个主题放在同一章因为它们的共性都是“跨越 Selenium 默认操作边界”。5.1 多窗口切换别再用 driver.close 当“万能钥匙”页面点击一个链接后新开 tab 或新开窗口这种场景在 OAuth 登录、第三方支付模拟、详情页预览里很常见。不处理窗口切换的话你会发现不管怎么定位元素就是找不到——因为 driver 还停留在旧窗口上下文里。标准流程是三步先记录当前窗口句柄再触发新窗口打开最后遍历句柄切换到目标窗口。# 1. 记录当前窗口句柄 original_window driver.current_window_handle # 2. 点击触发新开窗口的元素 driver.find_element(By.LINK_TEXT, 前往支付平台).click() # 3. 等待新窗口句柄出现 WebDriverWait(driver, 10).until( lambda d: len(d.window_handles) 1 ) # 4. 切换窗口 new_window [w for w in driver.window_handles if w ! original_window][0] driver.switch_to.window(new_window)注意切换窗口以后原来的元素变量全部失效需要重新定位。还有部分环境下浏览器会直接复用已有 tabtarget_self根本没有新句柄出现这种情况就不会走进这个分支。还有个小技巧页面标题更能代表目标窗口身份。如果开了两个新窗口它们的句柄顺序不稳定那可以用 title 判断for handle in driver.window_handles: driver.switch_to.window(handle) if 订单详情 in driver.title: break5.2 iframe 切换定位不到元素的第一嫌疑iframe 嵌套是自动化脚本的头号杀手比定位不懂 XPath 更常见。任何 find_element 找不到节点的时候我都建议先看这个元素在不在 iframe 里。判断方式在浏览器开发者工具里看元素样式如果是嵌入在 iframe 文档下的直接查源码外层有没有iframe标签。处理方案就是切换上下文# 切换到 iframe可以传 index、name/id 或定位元组 driver.switch_to.frame(driver.find_element(By.CSS_SELECTOR, iframe[src^https://pay])) # 或直接指定 index driver.switch_to.frame(0) # 操作完回到默认内容 driver.switch_to.default_content()如果 iframe 里还嵌着 iframe三层嵌套需要一层层切进去操作完再一层层切回来。有一种快速刺穿全部 iframe 的方法——但这个依赖元素就在某一个 iframe 里写起来较复杂很少用。日常就是逐层两步走driver.switch_to.frame(outer) driver.switch_to.frame(inner) # 此时可以定位内层元素 ... # 退回外层 driver.switch_to.parent_frame()switch_to.parent_frame() 是切回上一层 iframe而 switch_to.default_content() 是直接回到最外层文档两者用途不同别混了。Shadow DOM 的处理是另一个故事。新版 Chrome 的 Selenium 已经支持穿透 shadow-root 定位但写法稍不一样——用 driver.find_element 配合 shadow root 的向下查找。大部分情况下遇到 web component 封装的内部元素最好先找业务方确认是否暴露>element driver.find_element(By.ID, submit-btn) driver.execute_script(arguments[0].click();, element)页面滚动也可以交给 JS比 ActionChains 的 scroll_to_element 稳定driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) # 滚到具体元素位置 driver.execute_script(arguments[0].scrollIntoView({block: center});, element)去掉输入框的 readonly 属性、修改元素的禁用状态、设置日期控件的值都是 JS 大显身手的地方。举一个很常见的高频场景日期选择插件遮挡点不动直接通过 JS 赋值并触发 change 事件date_input driver.find_element(By.ID, date) driver.execute_script( arguments[0].value 2024-03-18; arguments[0].dispatchEvent(new Event(change, {bubbles: true}));, date_input, )JS 执行还经常用来取数据。比如你想验证页面某个接口返回的数据在 DOM 里有没有正确渲染可以result driver.execute_script( return document.querySelector(.total-amount).innerText; ) assert 188.00 in result需要注意的是execute_script 的参数注入必须写成 arguments[0] 这种形式不要直接拼接字符串否则容易踩转义和引号嵌套的问题而且可能引发注入风险。返回结果只有三种类型——直接量、WebElement 引用、数组/对象——传出来的字典结构要小心处理。5.4 截图与下载测试报告的硬依赖自动化脚本跑完总得有凭据。save_screenshot 是保存当前窗口截图element.screenshot 是截某个元素。常用写法driver.save_screenshot(reports/screenshots/failed_case.png) # 或者只截元素 element.screenshot(reports/screenshots/avatar.png)Allure 报告集成时把截图以字节流形式加到报告里是常规操作。实际项目中截图是定位问题的重要素材在断言失败或者异常捕获里自动截图能省下大量排查时间。try: wait_click(driver, By.ID, save-btn) except Exception: driver.save_screenshot(reports/error_ datetime.now().strftime(%Y%m%d_%H%M%S) .png) raise下载文件这块Selenium 4 提供了 set_download_path 设置默认下载目录配合 ChromeOptions 可处理下载弹窗和文件名变化。下载完成后用 os.path.exists 或 glob 来判断文件是否落地再配合文件大小不为0来确认下载完整性。prefs {download.default_directory: /tmp/downloads} options webdriver.ChromeOptions() options.add_experimental_option(prefs, prefs) driver webdriver.Chrome(optionsoptions)6. 高频报错排查把这些异常背下来项目成功一半最后这部分用速查表收尾把自动化测试里最常见的异常、原因和解决路径写成一张可以直接翻的表格。这份清单是我在实际项目和带团队过程中一点点积累的基本覆盖 90% 以上报错。异常常见原因处置方向NoSuchElementException元素不在 DOM、定位表达式错误、元素在 iframe 或 Shadow DOM 中先切 iframe/shadow root再确认定位表达式StaleElementReferenceException页面刷新或元素被重新渲染重新查找元素或改用 WebDriverWait locator 模式ElementClickInterceptedException元素被其他元素遮挡用 JS click、滚动到可见位置、或者先关闭遮挡元素ElementNotInteractableException元素存在但不可见/不可操作检查是否被 CSS 隐藏、是否在视口外、是否 readonlyTimeoutException显式等待超时条件一直未满足确认等待的条件写对没、元素是否在 iframe 里InvalidSelectorException定位表达式语法错误用浏览器的 console 验证 XPath/CSS 表达式WebDriverException驱动与浏览器版本不匹配、端口占用重新匹配 chromedriver 版本、重启相关进程SessionNotCreatedException浏览器崩溃或初始化失败清理浏览器缓存/配置检查 options 是否有冲突把表格里的每一项都当成排查手册来用效率会提升很多。后面补充几个大家特别容易反复踩的经验细节。第一个浏览器版本和驱动版本必须严格匹配。Chrome 浏览器一升级驱动器就得跟着换这属于周期性踩坑。建议把 chromedriver 的版本管理写成自动脚本定时去检查匹配版本必要时直接用 webdriver-manager 这类包自动管理。第二个失败重试和用例隔离。脚本里捕获异常后简单重试一次可以挡掉 80% 的偶发性失败但重试逻辑要设计好不能无限重试掩埋真实问题。用例之间还要注意状态隔离不要上一用例登录了下一用例还在登录态里跑互相影响非常难排查。每个用例的 tearDown 里做数据清理或者回滚操作是测试设计的基本功。第三个环境差异。同一套脚本本地 Chrome、CI 容器里的无头模式、远程 node 机器行为有时候不一样。一个典型的坑无头模式headless下元素尺寸和行为与有头模式不同需要特别关注元素可见性和下载行为。我通常在 CI 环境专门做一次“无头模式下的元素兼容确认”避免脚本只在本地通过。还有个工具层面的补充Selenium Manager 从 4.6 开始内建了自动驱动管理大部分情况下初始化 driver 时不用手动下载 chromedriver它会根据当前浏览器版本自动匹配。对于刚入门的人这个能省掉很多配置烦恼。但要注意内网环境或者集中执行的测试机可能需要手动指定 driver 路径这一点在团队环境里经常被忽略。7. 最后分享一个实战小技巧用封装代替散装代码从定位、交互、等待、上传到窗口和 iframe单个函数都知道怎么用了最后一步是把它们串成可复用的“页面操作库”。我见过太多测试工程源码每个用例里地方都长得差不多但都是复制粘贴的五行六行。建议的做法是给项目建一个 base_page 类把高频操作做成方法这样你的用例代码会很短、报错定位也极快。class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def find(self, by, value): return self.wait.until(EC.presence_of_element_located((by, value))) def click(self, by, value): self.wait.until(EC.element_to_be_clickable((by, value))).click() def input_text(self, by, value, text): element self.find(by, value) element.clear() element.send_keys(text) def switch_frame(self, locator): self.wait.until(EC.frame_to_be_available_and_switch_to_it(locator)) def get_text(self, by, value): return self.find(by, value).text具体业务页面继承这个基类把页面元素定位放到类属性里用例直接调方法代码结构一下就清晰了。这算不上什么高深设计但它是从“能跑的脚本”走向“扛得住回归的自动化项目”之间很关键的一步。再补一个建议日常写脚本时多留意那些稳定不变的元素属性data-testid、固定 class 前缀、id 命名规范这些往往是前端工程规范化的产物比随便写 XPath 可靠得多。跟开发团队约定好 ui 自动化测试专用标识很多稳定性问题能直接根治。文件上传、窗口切换、iframe、JS 兜底、显式等待封装这套组合拳打下来市面上绝大多数 web 自动化需求都够用了。剩下那些罕见特例等真遇到了再拿起这篇里的思路去扩展也不迟。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询