Selenium UI自动化测试实战:从元素定位到框架设计

发布时间:2026/10/20 17:55:20
Selenium UI自动化测试实战:从元素定位到框架设计 1. 项目概述1.1 核心需求解析做Web端的UI自动化Selenium基本是绕不开的那个框架。这几年我陆陆续续做过好几个基于Selenium的自动化测试项目从最初只会用find_element和click的“脚本搬运工”到后来能自己封装一套稳定的测试框架踩过的坑确实不少。这篇内容我尽量讲得实在一点把我实际用下来的经验、思路和一些非常规的技巧都交代清楚给正在学Selenium或者准备搭建UI自动化体系的朋友做一个靠谱的参考。这个项目核心解决什么问题一句话用程序代替手工在浏览器里自动执行重复性的功能验证操作。比如你每天要点一遍“登录→创建订单→查询订单状态”的流程手工点五天就会烦点二十天就容易漏。Selenium可以让机器去点并且把每一步的操作结果记录下来还能在关键节点自动截图、对比数据保证了回归测试的覆盖率和一致性。它适合谁来学习我觉得分三类人最受益。一类是刚入门测试开发想从手工测试转向自动化的同学Selenium是一个很好的切入点一类是在职测试工程师项目里回归测试量太大想找工具来解放生产力还有一类是开发转测试的本身懂代码但对UI自动化的设计思路不熟需要一个具体框架来落地。这三类人读完这篇应该都能找到自己想要的部分。1.2 项目目标与学习路线Selenium这框架从学习到能用再到了解它的设计原理大致是三个阶段。第一阶段是工具阶段你要会写脚本知道怎么定位元素、怎么操作浏览器、怎么断言结果不需要理解太多底层机制就能跑起来第二阶段是框架阶段你要开始思考脚本怎么组织、公共方法怎么封装、数据怎么管理、用例怎么设计这个阶段决定你的自动化项目能不能长期维护第三阶段是基础设施阶段你要懂Selenium的通信机制、懂得怎么跟CI/CD集成、怎么调度分布式执行这时候你已经可以给团队搭建一套完整的自动化测试平台了。我的建议是初学者不要一开始就把所有东西都学全先跑通一个最简单的“打开浏览器→点击元素→输入内容→断言结果→关闭浏览器”流程建立信心之后再逐步加码。很多人在第一步就卡住往往是因为环境问题或者定位方式太僵化这两个问题我后面会专门详述。2. 整体设计思路拆解2.1 为什么选择Selenium而不是其他方案做Web UI自动化技术选型上其实有不少选择。Playwright、Cypress、Selenium是这几年比较主流的三驾马车。我个人的看法是如果你做的是多浏览器兼容性测试尤其是要覆盖老旧浏览器的场景Selenium依然是首选。它的WebDriver协议已经是行业标准所有主流浏览器都原生支持这一点是Playwright和Cypress比不了的。另外一点是生态。Selenium出来这么多年网上的资料、社区的问答、历史踩坑记录都非常多。你遇到一个问题大概率别人早就遇到过了在社区里搜一下就能找到解决方案。对于刚入门的人来说这个“已有经验池”非常重要能帮你省掉很多折腾的时间。而Playwright和Cypress虽然功能更现代但遇到冷门问题的时候能查到的资料相对少对新手不太友好。不过我也要客观说一句Selenium确实有它的痛点。比如它的执行速度比Playwright慢一些对单页应用SPA的某些动态渲染场景处理起来需要多写一点等待逻辑还有它对Shadow DOM的支持不如Playwright顺手。但这些都是工程实现层面的问题可以通过框架设计来缓解不影响它作为入门首选框架的地位。2.2 与脚本化测试的本质区别很多人会误解觉得Selenium自动化就是把手工测试用例翻译成脚本。这种认知会导致后面项目做崩。我举一个例子如果你只是把“点击登录按钮”对应成一行click()把“输入用户名”对应成一行send_keys()那本质上这叫“录脚本”不叫“建框架”。真正的UI自动化测试框架要考虑的是这样几个问题第一元素定位的稳定性。同一个元素在不同的页面状态下的class是否变化动态加载的元素能不能稳定等到第二数据的可维护性。如果你的脚本里到处写着“admintest.com”这种硬编码数据一旦测试账号变更你要改多少个文件第三用例的组织方式。三四百条用例是写成一个个独立的函数还是用数据驱动的方式统一管理第四结果的可追溯性。用例跑挂了能不能清楚地看到是哪个步骤失败有没有截图日志打在哪里这些问题不解决脚本写越多维护成本越高最后变成团队里没人想碰的“屎山”。我在实际项目里见过太多这种例子了——自动化用例跑一遍要两个小时挂掉一半误报率极高最后被团队废弃。所以这篇文章后面讲设计思路的时候我会刻意强调“可维护性”这个核心目标工具只是手段稳定和可持续才是目的。2.3 选型与架构设计思路基于上述思考我在实际项目中采用的是一种分层架构思路简单说就是测试用例层→操作封装层→元素映射层→基础驱动层。测试用例层只管业务逻辑的编排操作封装层提供统一的动作API比如click、input、select、upload、switch_window等元素映射层把页面元素与定位信息解耦统一管理基础驱动层负责浏览器的启动、配置、关闭以及驱动生命周期管理。这种结构的核心好处是隔离变化。页面DOM结构变了你只需要改元素映射层的定位信息自动化框架升级了你只需要适配基础驱动层业务规则变了你只需要调整测试用例层。各层之间单向依赖不会互相污染。后文讲到核心模块的时候我会针对每一层展开讲讲具体怎么设计和实现。3. 核心细节解析与实操要点3.1 环境准备与版本匹配先聊环境因为我在新手阶段被环境问题卡过好几天后来才搞明白关键在版本匹配。Selenium的客户端库、浏览器驱动、浏览器本身三者之间是有版本对应关系的。举个具体的例子如果你用Chrome浏览器那Chromedriver的主版本号必须跟Chrome的主版本号一致。Chrome是115版本那就得下载115版本的Chromedriver否则启动浏览器的时候会直接报错。具体的版本匹配对照信息以大家能查到的线上信息为准我自己的经验是每个大版本更新的时候Selenium官方资源库和浏览器官方驱动仓库都会在很显眼的位置标出兼容版本自己先确认用哪个Chrome版本再去配套下载对应的驱动即可。驱动下载之后要注意文件存放路径最好放到一个固定目录并把该目录加入系统的PATH环境变量。Windows下我习惯把chromedriver.exe放在D:\tools\webdriver这个目录下然后系统环境变量里添加这个路径。这样后续脚本里就不需要每次传executable_path参数了。macOS下我习惯放在/usr/local/bin下面这个目录本身就在PATH里也比较顺手。还有一个很多人会忽略的点Selenium 4的某些版本对驱动管理做了简化新增了自动管理驱动的新机制。如果你是用的Selenium 4以上版本可以不用手动管理驱动路径让框架自动检测并下载匹配的驱动。这确实省心了不少但前提是你的网络环境允许Selenium访问远端仓库。对于严格的隔离网络环境还是手动指定驱动路径更可靠。3.2 元素定位从入门到进阶元素定位是UI自动化里最基本的功也是最重要的功。Selenium提供的定位方式有ID、Name、Class Name、Tag Name、Link Text、Partial Link Text、XPath、CSS Selector这几种。我的使用习惯是优先级排序ID Name CSS Selector XPath 其他。为什么这个排序因为ID在正常页面里是唯一的定位速度最快也最稳定。没有ID的情况下我优先尝试CSS Selector它的语法简洁、执行效率高。XPath虽然功能最强大但写得太长的话页面前端稍微改个结构就挂了可维护性差。我见过不少新手一上来就用“//*[idmain-content]/div[3]/div[2]/div[1]/button”这种全路径式XPath页面改一个层级就彻底失效这是典型的反面教材。如果必须用XPath建议用相对定位围绕元素的属性来写能不用层级就不用层级。比如你要定位一个“提交”按钮优先考虑“//button[contains(class,submit-btn)]”这种写法比“/html/body/div[2]/form/div[5]/button”稳得多。还可以用XPath的文本匹配比如“//button[text()确 定]”或者“//span[contains(text(),删除)]/parent::button”这在处理动态页面的时候很实用。另外有两个进阶技巧我要专门提一提。第一个是利用XPath的轴定位比如定位到某个元素之后通过parent、following-sibling、preceding-sibling去定位同级的其他元素。这种写法在表格操作里特别常用比如“点击某一行后面的修改按钮”就可以先定位到包含特定文本的行再通过轴定位找到那个按钮。第二个是页面对象模式下定位器可以定义为一个个元组统一放在页面类里后面讲框架设计的时候会展开。3.3 三大等待机制及选择策略等待是UI自动化里最大的坑也是最核心的必修课。Selenium里有三种等待方式强制等待time.sleep、隐式等待implicitly_wait、显式等待WebDriverWait。强制等待最直观就是代码里写死“睡2秒”但你根本不可能预判网络快慢和页面加载速度写少了偶尔崩写多了整个跑下来慢得离谱。我的建议是只在调试时用正式用例里尽量不要出现time.sleep。隐式等待是给WebDriver设置一个全局的轮询超时逻辑很简单在指定时间内不断尝试查找元素直到元素出现才继续走下一步。这个方案设置一次全局生效但它有两个明显的副作用。一个是它只对元素是否存在的判断生效对元素是否可见、可点击、处于某个交互状态就无法判断了另一个是隐式等待与显式等待混用的时候两者的轮询逻辑可能会冲突导致等待时间比预期更长或判断时机不准。所以在实际框架设计中我倾向于统一使用显式等待放弃隐式等待。显式等待是给特定元素设定专属的等待条件和超时时间。它的核心用法是配合Expected Conditions比如visibility_of_element_located、element_to_be_clickable、presence_of_element_located等。我自己的标准流程是这样点击一个按钮之前先等待这个按钮变为可点击状态输入框输入之前先等待它的可见和可用操作完之后再等下一个关键元素出现以此作为操作生效的标志。这套流程跑起来之后用例的稳定性会有质的提升。这里还要讲一个很多人不知道的原理那就是WebDriverWait在等待期间其实是一个轮询过程默认每0.5秒轮询一次如果超过设定的超时时间仍然未满足条件就抛出Timeout异常。你可以把轮询频率调得更细但实际项目里默认值已经够用不要过度优化除非你确实遇到慢加载和快判断之间的极端情况。其实还可以自定等待条件。比如页面上某个元素出现了但状态一直是“加载中”需要等到它的某个属性变化后才能继续操作。这种场景下Expected Conditions里没有现成的你可以实现一个自定义条件类或者用lambda函数写一个匿名条件。WebDriverWait的until方法接受任何返回非False值的可调用对象这个灵活性非常强。3.4 浏览器操作与常用API经验实际操作浏览器的时候有几个API我要单独强调一下因为它们的参数和细节如果不注意很容易产生低级错误。第一个是窗口管理。现在做Web端测试很多系统的业务页面是在新的Tab或者新窗口里打开的。Selenium的window_handles就是处理这类场景的关键。我习惯的操作顺序是window_handles获取当前所有句柄切换到新句柄完成操作后关闭新窗口再切回主窗口。这里面有个容易出现的问题如果你不切回主窗口后续所有定位操作都会作用在错误的上下文里报“no such window”之类的错误。另外Selenium 4废除了switch_to_window这个老API改成用driver.switch_to.window语法更统一但这让习惯老写法的人得注意一下。第二个是文件上传。input标签的文件上传用send_keys传入本地文件路径是最直接的方案。但很多现代系统的上传组件都是自定义的不是input标签这时有两种方案一种是建立规范时约束开发上传控件必须保留input元素支持标准上传另一种是用系统的文件对话框处理工具这种方案在无头模式下有时会出问题。我推荐第一种方案因为它最稳定也最容易维护。第三个是下拉框处理。Selenium的Select类封装了对select元素的处理支持按可见文本、按value属性、按下标三种方式选择。这里有个坑元素其实是一个React或Vue自定义下拉并不是原生select标签那Select类就不能直接用只能通过click展开再点击对应的选项元素来处理。第四个是页面滚动。滚动到页面底部、滚动到某个元素的场景很常见。用execute_script(window.scrollTo(0, document.body.scrollHeight))可以滚到底部定位某个元素之后用execute_script(arguments[0].scrollIntoView(), element)可以把元素滚动到可视区域。注意桌面端和移动端的滚动容器可能不同屏幕锁定在body上不一定能生效。4. 实操过程与核心环节实现4.1 项目初始化与依赖管理我一般建议用pip安装Selenium同时尽量配合虚拟环境管理项目的依赖。如果你还在用全局Python安装建议尽早切换不然不同项目之间的Selenium版本、第三方库版本总有互相冲突的一天。创建虚拟环境很简单在项目目录下执行python -m venv venv然后激活它再安装依赖这样所有包都隔离在这个项目内部。依赖清单我会单独列一个requirements.txt文件内容包括selenium和webdriver_manager再加pytest作为测试执行框架。webdriver_manager这个库可以自动下载和缓存浏览器驱动省掉了手动管理驱动的麻烦。这样任何人拉取项目代码之后只需要两行命令就能跑起来pip install -r requirements.txt然后启动测试。不过要注意的是pytest只是测试执行器它不是Selenium的核心依赖。你也可以用unittest但pytest的断言和fixture机制更灵活更贴近现代Python测试的生态。4.2 一个可复现的登录流程Demo为了让刚入门的朋友理解整个执行链路我写一个最简单的登录流程demo。注意这只是一个教学示例真实项目的框架设计会更复杂但整个执行路径是相通的。import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def test_login(): driver webdriver.Chrome() driver.get(https://example.com/login) wait WebDriverWait(driver, 10) # 等待用户名输入框可见并输入账号 username wait.until( EC.visibility_of_element_located((By.ID, username)) ) username.send_keys(tester01) # 等待密码输入框可见并输入密码 password wait.until( EC.visibility_of_element_located((By.ID, password)) ) password.send_keys(********) # 等待登录按钮可点击并点击 login_btn wait.until( EC.element_to_be_clickable((By.ID, login-btn)) ) login_btn.click() # 等待登录成功的标志元素出现 success_text wait.until( EC.visibility_of_element_located((By.CSS_SELECTOR, .user-info)) ) assert Tester01 in success_text.text, 登录后用户信息不符合预期 time.sleep(2) # 这里仅为演示真实框架不应该出现 driver.quit()这个demo里表达的核心思想是对每个关键交互动作之前都做了显式等待等待的是元素的可交互状态而不是固定sleep。很多人会问“那为什么最后还有一个time.sleep(2)”坦白说这是一个反例有些系统在登录成功后还会做重定向页面跳转过程中旧元素销毁、新元素尚未生成直接立刻定位新元素就可能失败。但正规的做法是继续用显式等待去等待新页面的某个关键元素而不是盲Sleep。这里我写出来只是为了说明很多人会在最后一步犯这个毛病。4.3 页面对象模型(POM)的设计与实现这个demo如果放到真实项目里我不会这么写。为什么因为真实的系统有几十个页面如果每个用例里都像这样直接写元素定位和操作逻辑用例之间会出现大量重复代码。一个按钮的定位信息从ID变成了class你至少要改几十个地方还容易漏。所以我在实际项目中一定会用页面对象模型POM来管理。POM的核心思想很简单为每个页面创建一个类把该页面相关的元素定位器By元组和操作方法比如输入用户名、点击登录、获取错误提示封装在类里。测试用例只跟页面对象交互不直接接触具体的定位信息。这样做的效果是页面变了我只需要改对应的页面类用例逻辑保持稳定维护成本大幅下降。我举一个实例登录页面的页面类大致长这样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) # 元素定位器统一放在类属性中 self.username_input (By.ID, username) self.password_input (By.ID, password) self.login_button (By.ID, login-btn) def enter_username(self, text): el self.wait.until(EC.visibility_of_element_located(self.username_input)) el.clear() el.send_keys(text) def enter_password(self, text): el self.wait.until(EC.visibility_of_element_located(self.password_input)) el.clear() el.send_keys(text) def click_login(self): el self.wait.until(EC.element_to_be_clickable(self.login_button)) el.click() def login(self, username, password): 登录业务动作的组合方法 self.enter_username(username) self.enter_password(password) self.click_login()那测试用例端就可以写得很干净def test_login_success(driver): login_page LoginPage(driver) login_page.login(tester01, ********) home_page HomePage(driver) assert home_page.get_username_display() Tester01看到差异了吗测试用例里已经完全没有By.ID、By.CSS_SELECTOR这些定位信息了全部收敛到页面类。如果再结合测试数据驱动把用户名、密码、预期结果都放到测试数据文件里那整个用例文件基本就剩下“动作调用断言”维护成本非常低。我还遇到过一种情况不同页面下会有公共的UI组件比如页头的用户信息、全局搜索框、底部的版权信息等。这可以在POM之外做一个组件类的抽象比如HeaderComponent类专门封装页头相关的操作然后在多个页面类里通过组合方式引用。这么做能进一步减少重复代码但代价是抽象层级增加对团队整体能力要求也更高。我建议初学者先把基本的POM跑熟组件抽象是进阶阶段再考虑的事。4.4 数据驱动与配置集中管理数据驱动是我做UI自动化项目必不可少的一环。简单理解就是把测试用例中使用到的数据从脚本里抽出来放到配置文件或测试数据文件中测试执行时通过参数化机制动态传入。这么做的直接动因是回归测试要覆盖多种账号类型、多种数据输入场景如果每个数据组合都写成一个独立用例函数那代码量会爆炸。我用excel或者yaml文件维护测试数据pytest结合参数化实现数据驱动用例数据与业务逻辑完全分离。比如登录用例我可以维护一组测试数据正确账号正确密码、正确账号错误密码、未注册账号任意密码、账号为空密码为空统一作为参数传入同一个测试函数。这样写出来的用例既简洁又能保证一条数据对应一条用例结果后续复盘也方便定位具体哪组数据失败。配置管理也很关键。浏览器的类型、隐式等待超时时间、基础测试地址、是否启用无头模式这些都应该放在一个独立的配置文件里而不是散落在代码里。我在项目中习惯用一个config.py来集中管理这些配置并且支持从系统环境变量里覆盖默认值。比如在本地开发时用有头模式方便调试在CI流水线里用无头模式节省资源这个差异就通过环境变量来控制。4.5 断言与结果回溯体系UI自动化的断言不能只做“最终结果对比”这句话我要专门解释一下。很多人写断言只判断最后出现的某段文本是否符合预期一旦失败只能看到“AssertionError”和一行代码定位根本不知道当时页面是什么状态、操作执行到哪一步了、数据是否已经进入系统。这种项目排查问题极费劲跑一次挂了排查两个小时才能定位问题。我的做法是这样在框架层统一封装一个断言工具类每次断言失败时自动捕获两个关键信息一是整个页面当时的截图保存到以时间戳命名的png文件二是当前浏览器的DOM结构片段保存到html文件。这样用例挂掉之后直接打开截图和DOM文件基本能还原现场排查效率大幅提升。另外核心业务链路操作的关键步骤节点我也都会在日志里做标记比如“输入用户名”“点击登录”“等待首页元素出现”的完成时间和耗时。这么做以后如果你发现某条用例总是执行很慢通过日志可以直接看出是哪个步骤耗时异常是很常用的性能定位手段。5. 常见问题与排查技巧实录5.1 经典报错与根源分析我把这几年来在Selenium项目中最常遇到的报错和排查思路整理了一下做成一个表报错信息常见根源排查与解决思路NoSuchElementException定位方式错误或元素未加载完成先用重试等待确认元素是否存在检查定位表达式是否复制错误用开发者工具确认元素是否在frame或shadow DOM中ElementNotFound元素不在当前窗口或frame排查是否有多窗口、多iframe如果是frame需要先switch进去才能定位内部元素ElementNotInteractableException元素存在但不可交互可能是被遮挡、不可点或禁用检查元素状态尝试滚动到可视区域再用display操作StaleElementReferenceException你拿到的元素引用已经失效页面刷新或DOM更新重新查找元素再执行操作使用显示等待配合EC.presence_of_element_locatedTimeoutException显示等待超过设定时间分析等待条件是否匹配还是元素确实没有按预期出现检查网络加载和渲染耗时SessionNotCreatedException驱动版本和浏览器版本不兼容检查WebDriver与浏览器主版本号是否一致检查日志里提示的具体要求版本InvalidSelectorException定位表达式语法错误在开发者工具里实测表达式确认可以匹配到元素后再写入代码InvalidArgumentException传入参数类型或格式错误仔细看抛错上下文多半是某个API的参数值不对例如需要传元组却传了字符串这个表不可能覆盖所有情况但覆盖了我遇到的所有高频问题。新手可以先收藏遇到报错的时候对照着排查大概率能找到方向。5.2 StaleElementReferenceException的深度解析这个异常值得单独拿出来讲因为它太普遍了而且报错信息很有迷惑性。简单说StaleElementReferenceException表示你代码里持有的那个元素对象已经从DOM上移除或重新渲染过了你再用这个旧引用去操作就会报错。典型场景是什么你点击了一个“添加行”的按钮页面上动态插入了新内容之前引用的某行元素被重新渲染了或者你点击了一个“删除”按钮数据行被移除而你想再去读这个行里的文本。一旦遇到这种情况很多人的第一反应是加等待但等待解决不了问题——因为DOM已经变了不是时间问题是引用失效的问题。正确的做法是在操作前重新查找元素。换句话说你把“查找元素”和“对元素做操作”分成两步如果操作时发现引用无效就重新查找一次再继续。我在框架里封装了一个safe_click方法它的逻辑就是先尝试点击如果捕获到StaleElementReferenceException就重新定位元素再点击一次重试最多三次。这个方案对付大部分动态页面场景都非常有效。5.3 iframe与多窗口切换排查iframe处理是UI自动化里另一个让人抓狂的点。有些老系统特别喜欢用iframe嵌套页面你明明通过开发者工具看到了元素但脚本就是报找不到元素。原因很简单Selenium的原生定位方式只认当前文档上下文如果你不在对应的iframe里DOM对你来讲就是不可见的。处理思路很清晰。先统计当前页面有多少个iframe。用driver.find_elements(By.TAG_NAME, iframe)看一下。然后driver.switch_to.frame()可以按索引、name属性或元素对象三种方式切入。操作完成后必须切回默认内容driver.switch_to.default_content()。如果iframe还有嵌套层那每一层都得依次切入逐层退出时也要一层一层来。多窗口切换也是高频问题。记住一个原则窗口句柄是一个集合新开的窗口句柄一定在这个集合的末尾。所以我的标准操作是先记录当前句柄点击“打开新窗口”按钮后等待句柄数量增加然后取新句柄并切过去。这里千万不能直接取集合[-1]然后立刻切换因为窗口可能还没加载完成切换过去后页面是空白。这一点用显式等待配合expected_conditions来守候新窗口的某个元素最稳妥。5.4 调试技巧与日志策略最后讲调试。UI自动化调试有一个特点跑一次用例要几十秒甚至几分钟如果靠着每改一行代码就跑一遍效率极低。我的习惯是尽量用开发者工具来辅助调试定位语句。在浏览器里按F12打开开发者工具在Console里用document.querySelector或$x(//xpath)实测当前页面结构确认表达式有效后再写入脚本。这样把“在代码里反复试错”提前到“在浏览器里验证”能省不少时间。还有一个技巧是可以在关键操作之间加入调试输出比如打印当前页面的URL、打印当前页面标题、打印某个元素的文本内容。这些信息在排查“用例不知道走到哪一步就挂了”的场景下非常有用。关于日志我强烈建议引入logging模块而不是print因为logging可以分级、可以输出到文件还可以带上时间戳。print在调试时确实方便但在大量用例并发执行的情况下print的内容会互相穿插根本没法看。logging的输出从INFO到DEBUG到ERROR都清晰可辨这对排查问题很重要。6. 集成与持续迭代6.1 接入CI流水线的实践UI自动化跑在本地只能算验证真正发挥价值要接入持续集成让代码每次合并后自动触发回归测试。我推荐的做法是把测试用例的入口命令集成到CI的Jenkinsfile或GitLab CI的pipeline里。接入前一定要想清楚两个前置条件。一个是无头模式的问题CI服务器上没有真实的显示器必须在配置里启用浏览器的headless模式否则启动浏览器就会报错。headless模式下虽然不会弹出浏览器窗口但所有渲染和交互逻辑都会正常执行唯一需要注意的是某些特效动画在headless下表现可能不同。另一个是资源占用问题一个Chrome实例大概占用几百兆内存如果并发执行的用例较多CI机器的内存要提前规划好否则卡死是常态。CI里跑UI自动化还有一个重要问题失败重试和稳定性。完全不放宽的话我建议自动重试失败用例一次尤其是网络波动导致的偶发失败重试往往能直接通过。我这里说的是偶发失败如果是稳定回放失败那一定是代码或数据处理逻辑有问题重试再多也没有用。6.2 版本升级与框架演进路径Selenium的版本升级是一个需要谨慎的过程不要轻易为了尝鲜去升但也不要一直停留在老版本不走。以从Selenium 3升级到Selenium 4为例API上最明显的变化是元素定位语法从find_element_by_*系列函数改成了find_element(By.XXX, value)的统一写法还有switch_to和ActionChains的部分API重构。如果不改写代码直接切到Selenium 4运行时会有大量DeprecationWarning且部分方法失效。我建议升级遵循三步走的策略。第一步先小范围验证在一台机器上装好新版本Selenium跑一个最小冒烟用例确认核心功能没有问题。第二步全局搜索老API调用点统一改写。第三步完整回归整个自动化用例集对比新旧版本的通过率和耗时差异。如果通过率没有明显变化就可以放心升级如果出现大量失败优先排查定位API、等待行为差异这两块是升级影响最大的地方。6.3 个人实践经验与扩展思考做到这里我再分享一些不太会在教科书里看到的个人心得。第一点UI自动化的价值依赖于应用本身的UI稳定性。如果前端同事每天都在改样式和DOM结构你的自动化用例就会天天跟着改最终消磨掉团队所有人的热情。所以做UI自动化之前最好跟开发团队约定好页面自动化测试的契约重要的交互元素需要有稳定的标识比如稳定的id或data-testid属性而不是那种自动生成的随机类名。这不是自动化工程师单方面能解决的需要团队协作。第二点不要把UI自动化当成唯一的测试手段UI层级做业务逻辑校验其实是性价比偏低的因为它的执行速度慢、环境依赖强。一个合理的分层是接口层面覆盖核心逻辑UI层面只做端到端的关键链路冒烟单元测试交给开发。这样既能保证核心逻辑的稳定性又能控制UI自动化的维护成本。我见过一些团队把几百条接口能测的场景全部压在UI层最终就是自动化项目从“提效”变成“负担”。第三点自动化用例的数据准备与清理必须有机制保障。测试数据如果不清清第二次跑同一个用例的时候页面数据一多定位和断言就会混乱。我在实践中的做法是每个测试用例执行之前通过接口或者数据库直接创建独立的前置数据用例执行完之后清理数据保证每次运行环境的纯度。这样虽然多写了一些代码但整体稳定性高了一大截。第四点多浏览器兼容验证的价值建议控制在关键冒烟用例层面。全量用例在每个浏览器上都跑一遍时间成本非常高性价比很低。我在团队里推行的策略是日常回归用Chrome跑全量发布前冒烟时用Edge和Firefox跑核心链路这样兼容性的覆盖和自动化效率能有一个相对更均衡的状态。最后说一个我踩过多次的坑不要把验证逻辑完全寄托在Happy Path上。UI自动化测试要尽量设计“反向用例”比如登录失败时应该出现什么提示、提交空表单时应该怎么拦截、服务端异常时前端是否会有友好提示。这些反向用例看起来很偏门但它们往往才是线上事故的元凶。在自动化框架里把正向和反向用例用数据驱动的方式放在一起维护既能直观对比也能减少遗漏。做Selenium这几年我最大的体会是这个框架的门槛其实很低但天花板很高。学会写脚本只需要一周能不能设计出一套可长期迭代的框架才真正拉开差距。希望这篇分享能帮你在学习和落地Selenium的路上少走一些弯路。

关于本文作者

来自尧图内容编辑团队

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

尧图内容编辑团队

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

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

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

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

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

网站改版的5个关键决策

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

获取专属建站方案

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

立即免费咨询