ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Selenium自动化测试实战:框架原理、用例稳定化与工程落地

Selenium自动化测试实战:框架原理、用例稳定化与工程落地 1. 从实际项目选型说起为什么自动化测试框架里 Selenium 至今还是主流做 Web 自动化测试的人几乎没人绕得开 Selenium。哪怕现在各种新一代工具层出不穷Playwright、Cypress 讨论热度很高但你去招聘网站上看要求里写得最多的仍然是 Selenium。这背后其实有一个很朴素的原因Selenium 支持的浏览器最全社区沉淀最厚踩坑资料最多团队接手成本最低。我最早接触 Selenium 是在接手一个电商系统的回归测试时。当时项目里几百个核心用例全靠手工点每次发版前要花整整两天走主流程人累不说漏测还经常发生。后来的解决方案就是引入 Selenium 做 UI 层自动回归。那时候选型对比过 QTP但 QTP 那套商业工具太重License 费用高脚本绑定 Windows 环境对后来要跑的 Linux CI 流水线完全不友好。相比之下Selenium 免费、开源、跨浏览器、支持 Java / Python / C# / Ruby / JavaScript 等多种语言团队里随便谁都能上手自然就成了首选。很多人会把 Selenium 当成测试脚本工具其实它是一整套自动化测试框架的基座。它给你提供的能力包括浏览器自动化驱动、元素定位 API、事件交互模拟、页面状态等待、测试结果回传等。你要做的是在它之上搭建适合自己团队的用例组织方式、数据管理方式和报告体系。这篇内容我会从 Selenium 的组件结构讲起到环境搭建、脚本编写、稳定性优化再到框架分层设计最后给出一份可以直接参考的实践路径。不管你是刚接触自动化测试的新人还是已经在写脚本但总感觉用例不够稳的同学都能从中找到对应的解法。2. Selenium 家族全景WebDriver、IDE、Grid 各自扮演什么角色很多人以为 Selenium 是一个工具其实它是一个工具家族。官方把 Selenium 分成三个核心项目Selenium WebDriver、Selenium IDE、Selenium Grid。理解这三者的分工比急着装库写脚本更重要因为很多团队用不好 Selenium根源就是没搞清这三个组件的边界。2.1 WebDriver——真正的主角Selenium WebDriver 是自动化能力的核心。它通过浏览器厂商提供的驱动ChromeDriver、GeckoDriver、EdgeDriver 等来与浏览器进行通信直接调用浏览器内部的自动化接口模拟用户的真实操作。用一句大白话解释 WebDriver 的工作原理它不是靠截图找坐标的野路子而是通过 JSON Wire Protocol现在已演进为 W3C WebDriver 标准给浏览器驱动下达命令比如打开这个网址找到这个按钮点击它输入这段文字。浏览器驱动收到命令后会真实地在浏览器里执行这些动作并返回页面状态。这个设计带来的最大好处是操作接近真实用户。因为命令走的是浏览器原生自动化通道不是模拟鼠标键盘在屏幕上的坐标位置所以窗口遮挡、屏幕分辨率变化都不会影响执行结果。这也是 Selenium 能在多浏览器上稳定复用的原因。2.2 IDE——录制回放快速验证思路Selenium IDE 是浏览器扩展形式的录制回放工具。以前它叫 Firefox 插件现在官方推出了 Chrome 版本并且改成了支持导出脚本的模式。IDE 的价值在于当你对要自动化的流程还不熟悉时可以先把手工操作录制一遍生成初始脚本骨架再导出成 Python / Java / C# 等语言的 WebDriver 代码在此基础上做二次开发。不过我需要提醒你IDE 适合做快速探索和浏览器端的调试辅助不适合直接当作生产级测试框架。录制出来的脚本通常充斥着不稳定的绝对路径定位而且没有办法处理复杂业务逻辑比如动态订单号、随机验证码、跨步骤数据传递等。实战中我的用法一般是用 IDE 快速确认某个复杂操作序列在页面上可行然后把录制脚本当作参考按项目规范重构用例。2.3 Grid——分布式执行的基石Selenium Grid 解决的是多个浏览器、多台机器、并行执行的问题。它的架构分为 Hub 和 Node 两部分Hub 负责接收测试请求并分发给注册的 NodeNode 可以运行在不同操作系统上配置不同的浏览器版本。以前 Grid 配置比较繁琐需要单独下载 selenium-server 并手工注册节点。现在 Selenium 4 把 Grid 内置到了发行包里一条命令就能启动还支持 Docker 方式拉起浏览器容器配合云测平台也很方便。举个例子如果你们的测试用例需要覆盖 Chrome、Firefox、Edge 三个浏览器而本机只有 Chrome那就可以用 Grid 搭一个节点池分别在不同的虚拟机或容器里装不同浏览器调度层统一分发。这样既能做跨浏览器兼容验证又能把同一套用例分发到多节点上并行跑节省整体回归时间。3. 从零搭建 Selenium 测试环境版本兼容性与依赖管理的坑环境搭建是很多人第一个劝退点。网上一搜 Selenium 安装教程看起来就是一行 pip install selenium但实际跑起来各种报错多数问题都出在浏览器驱动版本与浏览器版本不匹配上。3.1 Selenium 安装的基础操作如果你用的是 Python 生态最常用的安装方式是 pippip install selenium如果你需要特定版本可以指定版本号pip install selenium4.21.0这里我建议不追最新只求稳定。Selenium 4.x 是目前的主流版本API 相对成熟网上资料也最多。你可以在命令行里执行pip show selenium查看当前版本。Java 项目则通过 Maven 或 Gradle 引入依赖以 Maven 为例dependency groupIdorg.seleniumhq.selenium/groupId artifactIdselenium-java/artifactId version4.21.0/version /dependency3.2 浏览器驱动版本匹配是个态度问题Selenium 本身只是客户端库它需要借助各浏览器的 WebDriver 实现来操作浏览器。常见驱动对应关系如下浏览器驱动名称获取方式ChromeChromeDriver从 Chrome for Testing 页面下载FirefoxGeckoDriverGitHub 官方仓库发布页下载EdgeEdgeDriverMicrosoft 官方站点下载Safarisafaridriver系统自带需开启允许远程自动化关键问题是Chromedriver 和 Chrome 浏览器版本必须保持主版本一致。比如你本地 Chrome 是 126 版本那就必须找 126 开头的 ChromeDriver。如果你这里匹配不上启动时大概率会看到类似的报错SessionNotCreatedException: This version of ChromeDriver only supports Chrome version 114我见过不少新手在这里反复折腾本质原因就是去网上随便下了一个 ChromeDriver没看版本号。那么有没有自动管理驱动的方案有的。如果你用 Python可以装一个webdriver-manager库它会自动检测浏览器版本并下载对应驱动pip install webdriver-manager使用示例from selenium import webdriver from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice)Java 生态也有类似的 WebDriverManager 库io.github.bonigarcia。如果你的测试环境经常变化、开发机浏览器升级频繁这个库能省掉很多烦恼。3.3 配置浏览器选项的正确姿势环境搭建时还有一步经常被忽略设置浏览器选项。很多人写代码只写一个webdriver.Chrome()跑起来后会弹出真实的浏览器窗口不稳定且干扰操作如果放在服务器上还容易因为没有图形界面而报错。我常用的 ChromeOptions 配置如下from selenium.webdriver.chrome.options import Options options Options() options.add_argument(--headlessnew) # 无头模式服务器上跑必备 options.add_argument(--no-sandbox) # Linux 环境避免权限限制 options.add_argument(--disable-dev-shm-usage) options.add_argument(--window-size1920,1080) # 固定窗口尺寸避免页面布局错乱 options.add_argument(--ignore-certificate-errors) # 测试环境常需要 driver webdriver.Chrome(optionsoptions)--headlessnew是 Chrome 109 之后的写法相比旧版 headless它更接近有头模式的页面渲染效果。如果你不想看到浏览器弹窗干扰你写脚本也可以直接用这个模式。3.4 环境变量的坑PATH 里找不到驱动还有一类常见问题是把驱动下载好之后启动脚本提示WebDriverException: Message: chromedriver executable needs to be in PATH.这个问题的解法有两种。第一种是把驱动目录加入系统环境变量 PATH。第二种是在代码里手动指定驱动路径个人更推荐这种方式因为明确、可迁移、不污染系统环境from selenium.webdriver.chrome.service import Service service Service(/path/to/chromedriver) driver webdriver.Chrome(serviceservice)如果你用了 webdriver-manager这个步骤也被省了它会把驱动路径直接注入到 Service 里。4. 手写第一个 Selenium 脚本定位、交互、断言的完整链路环境搭好之后最兴奋的时刻就是写出第一个能跑的脚本。我给你一个最简例子以 Python 为例打开一个搜索页面输入关键词并点击搜索按钮。from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time # 初始化浏览器 driver webdriver.Chrome() driver.get(https://www.baidu.com) driver.maximize_window() # 定位输入框并输入内容 search_input driver.find_element(By.ID, kw) search_input.send_keys(Selenium 自动化测试) # 定位搜索按钮并点击 search_button driver.find_element(By.ID, su) search_button.click() # 等待结果加载 time.sleep(3) # 断言页面标题 assert Selenium 自动化测试 in driver.title print(测试通过, 页面标题:, driver.title) driver.quit()这个脚本麻雀虽小五脏俱全但实际上产环境里你不会这么写。问题主要有两个一是time.sleep(3)这种固定等待太粗暴二是断言逻辑过于简单。后面章节我会展开讲怎么改。4.1 元素定位的八种方式选对才能写得稳Selenium 提供了八种元素定位方式我用表格整理一下定位方式方法适用场景IDfind_element(By.ID, id)元素有唯一 id优先级最高Namefind_element(By.NAME, name)表单元素常用Class Namefind_element(By.CLASS_NAME, class)一组同类型元素返回第一个Tag Namefind_element(By.TAG_NAME, input)不常用容易命中多个Link Textfind_element(By.LINK_TEXT, 完整链接文字)精确匹配 a 标签文本Partial Link Textfind_element(By.PARTIAL_LINK_TEXT, 部分文字)模糊匹配 a 标签文本CSS Selectorfind_element(By.CSS_SELECTOR, #id .class)前端工程师最熟悉性能好XPathfind_element(By.XPATH, //input[idkw])没有 id / class 时可兜底这里我给新手一个建议定位优先级按 ID name CSS Selector XPath 来选。CSS Selector 能不用尽量别用 XPath 吗也不是。XPath 的问题在于它受页面结构变动影响大比如多了个 div 层级绝对路径 XPath 就崩了而 CSS Selector 通常更简洁稳健。不过有些场景确实只有 XPath 能干比如根据文本内容定位按钮、定位包含特定属性的元素。# 通过 XPath 文本定位 button driver.find_element(By.XPATH, //button[contains(text(), 立即登录)]) # 通过 CSS Selector 定位 input_box driver.find_element(By.CSS_SELECTOR, input[nameaccount])4.2 交互操作的边界模拟用户行为而不只是按键Selenium 的交互不限于输入和点击。它支持键盘事件、鼠标事件、下拉选择、文件上传、拖拽等但有些操作的处理方式经常有人写错。下拉框选择很多人会用 find_element 点击再遍历选项点其实有更优雅的 Select 类from selenium.webdriver.support.ui import Select select_element Select(driver.find_element(By.NAME, city)) select_element.select_by_visible_text(北京) select_element.select_by_value(beijing) select_element.select_by_index(1)鼠标悬停、右键、双击需要用到 ActionChainsfrom selenium.webdriver.common.action_chains import ActionChains actions ActionChains(driver) target driver.find_element(By.CLASS_NAME, nav-item) actions.move_to_element(target).perform()文件上传分两种情况。如果是input typefile标签直接用 send_keys 传文件路径即可不需要真的模拟点击选择文件窗口upload_input driver.find_element(By.ID, file) upload_input.send_keys(/Users/test/report.xlsx)如果是非标准控件可能真的会弹出系统文件对话框这种 Selenium 是无能为力的一般需要借助 AutoIT 或 pywin32 这类操作系统级工具。在设计自动化用例时尽量引导开发把上传控件改成标准 input 实现能省很多事。4.3 断言怎么做才专业很多人写断言就用 Python 的assert但这有个隐患一旦断言失败测试框架收集错误信息会很粗糙而且你不太容易看出是哪个步骤出了问题。如果你用 pytest 来组织用例推荐用 pytest 的断言增强和钩子函数import pytest def test_search(): driver.get(https://www.baidu.com) driver.find_element(By.ID, kw).send_keys(自动化测试) driver.find_element(By.ID, su).click() assert 自动化测试 in driver.title, f页面标题异常实际为: {driver.title}pytest 会捕获断言失败并输出详细的上下文。如果你的团队有自己的测试报告系统建议在断言失败时做两件事截图保存现场、记录当前页面 URL。这是排查问题最有用的两个线索。def take_screenshot(driver, name): driver.save_screenshot(f./screenshots/{name}.png)pytest 里可以在 conftest.py 中通过 fixture 配置失败截图钩子下面是一种参考写法import pytest from selenium import webdriver pytest.fixture def driver(): driver webdriver.Chrome() yield driver driver.quit() pytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield report outcome.get_result() if report.when call and report.failed: driver item.funcargs.get(driver) if driver: driver.save_screenshot(f./screenshots/{item.name}_failed.png)这样每次失败自动留证极有利于排查问题。5. 能跑和稳定能跑之间的差距等待策略与页面状态写自动化时间久了你会发现用例失败的头号原因不是定位不对而是页面元素还没加载完脚本就急着去操作。网络慢的时候尤其明显。所以等待策略是 Selenium 脚本稳定性的第一课。5.1 三种等待方式实质区别是什么Selenium 里有三种常见的等待方式第一种是前面提到的time.sleep(3)强制线程休眠固定时间。它的最大问题是固定时间不可控3 秒可能不够也可能太多了——如果页面 0.5 秒就加载完你还得白等 2.5 秒如果第 3.5 秒才加载完它照样报错。这属于最不聪明的等待。第二种是隐式等待driver.implicitly_wait(10)。它设置了一个全局轮询超时时间每次 find_element 找不到元素时会在超时时间内反复尝试查找。它的问题也很明显全局生效一旦某些操作本身就不需要长时间等待也会被拖慢而且在 DOM 状态复杂时隐式等待的表现力有限。第三种是显式等待WebDriverWait这是最推荐的方式。它针对特定条件、指定超时时间和轮询间隔精确控制等待粒度。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, timeout10, poll_frequency0.5) element wait.until(EC.visibility_of_element_located((By.ID, result)))这行代码的意思是最多等 10 秒每 0.5 秒检查一次直到 id 为 result 的元素可见为止。这比固定 sleep 高效得多也比隐式等待精准。我在实际项目中几乎只用显式等待。5.2 常见的 Expected Condition 该怎么选expected_conditions模块提供了一组预设的等待条件常用的整理如下条件用途visibility_of_element_located元素可见宽高不为 0presence_of_element_located元素出现在 DOM 中不要求可见element_to_be_clickable元素可见且可点击text_to_be_present_in_element元素文本包含指定内容title_contains页面标题包含指定内容alert_is_present弹窗出现staleness_of元素已从 DOM 中移除常用于判断加载完成这里要重点说一个细节presence_of_element_located和visibility_of_element_located不是一回事。前者元素可能在 DOM 里但被 CSS 隐藏或者还在渲染中后者才代表用户真的能看到它。对于点击类操作我建议一律用element_to_be_clickable因为它更严格能降低误点击概率。5.3 Frame、多窗口和 Shadow DOM跳过的坑都要补上做自动化最怕的就是元素明明在页面上但定位不到。十有八九是遇到了 iframe 嵌套。iframe 相当于页面里的子页面Selenium 默认只认识顶层文档所以你需要先切换进去。driver.switch_to.frame(frame_id) # 根据 name/id 切换 driver.switch_to.frame(0) # 根据索引切换 driver.switch_to.default_content() # 切回主文档当前端用了 Shadow DOM 技术封装组件时里面的元素也定位不到要用 JavaScript 穿透shadow_host driver.find_element(By.CSS_SELECTOR, my-component) shadow_root driver.execute_script(return arguments[0].shadowRoot, shadow_host) inner_button shadow_root.find_element(By.CSS_SELECTOR, button.btn-primary)这些边界情况虽然不是每个项目都会遇到但一旦遇到而不会处理排查成本极高。提前了解比临时翻文档效率高得多。5.4 页面状态变化如何判断加载完成除了元素级等待还有一类问题是判断整个页面是否加载完成。一个常见误区是只判断document.readyState complete但在现代前端工程化页面里这个标志位早已不代表内容渲染完成因为很多内容是通过异步接口动态塞进去的。我的做法是结合业务特征设定完成标志。比如等待某个关键数据表格出现、等待 loading 遮罩消失、等待某个网络请求对应的占位文案被替换为真实文案。你可以先把这些条件抽象成函数然后统一封装在框架里后面章节会提到。6. 把脚本组织成框架Pytest Page Object 数据分离当你手里有三四十个脚本的时候还靠复制粘贴起步维护成本已经很高了。脚本之间公共的操作逻辑重复一旦页面结构变化你要一个个改。这时候必须从写脚本走向搭框架。6.1 为什么需要 Page Object 模式Page Object 是 Selenium 官方推荐的页面对象模型核心思想是每个页面封装成一个类页面上所有元素定位和页面操作都定义在类里测试用例只调用方法不直接接触定位器。举个例子。假设有一个登录页面你可能会把元素定位和登录操作封装起来class LoginPage: def __init__(self, driver): self.driver driver self.username_input (By.ID, username) self.password_input (By.ID, password) self.login_button (By.ID, loginBtn) def input_username(self, username): self.driver.find_element(*self.username_input).send_keys(username) def input_password(self, password): self.driver.find_element(*self.password_input).send_keys(password) def click_login(self): self.driver.find_element(*self.login_button).click() def login(self, username, password): self.input_username(username) self.input_password(password) self.click_login()测试用例就可以写成这样def test_login_success(driver): login_page LoginPage(driver) login_page.login(user001, Passw0rd) assert 个人中心 in driver.title这样做的最大价值是当页面结构变化时你只需要修改 LoginPage 里的定位器所有依赖它的用例都不需要动。这就是封装变化的意义。尤其 UI 自动化中页面调整非常频繁没有这层封装改动量会成倍增长。6.2 数据分离用例代码里不要写死测试数据测试框架成熟到一定程度后你要考虑数据从哪里来。常见的做法有几种常量配置比如基础 URL、超时时间放到配置文件里批量用例数据放到 Excel / YAML / JSON 里测试账号密码等敏感信息放到环境变量或密钥管理服务里用 pytest 的数据驱动机制可以把用例和测试数据彻底分开。这里我推荐用pytest.mark.parametrizeimport pytest from pages.login_page import LoginPage pytest.mark.parametrize(username,password,expected, [ (user001, Passw0rd, 登录成功), (user002, WrongPass, 用户名或密码错误), ]) def test_login(username, password, expected, driver): login_page LoginPage(driver) login_page.login(username, password) assert expected in login_page.get_tip_text()6.3 fixture 管理浏览器生命周期pytest 的 fixture 是管理 WebDriver 生命周期的最佳工具。在 conftest.py 中定义driverfixture让它成为一个可被子类化重用的基础设施import pytest from selenium import webdriver from selenium.webdriver.chrome.options import Options pytest.fixture(scopefunction) def driver(): options Options() options.add_argument(--headlessnew) options.add_argument(--window-size1920,1080) driver webdriver.Chrome(optionsoptions) driver.implicitly_wait(2) yield driver driver.quit()通过scopefunction保证每个用例都是干净的浏览器环境避免用例间状态污染。如果你有需要复用登录状态的场景可以再写一个logged_in_driverfixture依赖driverfixture 并完成登录操作。6.4 分层设计到底分几层一个比较成熟的 UI 自动化框架通常分为四层层级作用例子基础层封装 WebDriver 初始化和公共操作截图、日志、等待工具页面对象层页面元素和页面操作LoginPage、HomePage业务流层跨页面的业务流程下订单、支付流程测试用例层业务断言和测试逻辑test_login、test_order你不需要一上来就四层全部铺开但至少要有页面对象层否则脚本写多了肯定要返工。我的经验是先跑通 20 个核心用例然后再逐步分层重构。如果一开始就追求完美架构容易陷入过度设计反而拖慢进度。7. 稳定性提升三板斧失败重试、日志留痕、执行策略UI 自动化天生脆弱这谁都没法否认。网络抖一下、前端资源加载慢一点、第三方服务抽风一下用例就可能挂掉。这是正常现象关键在于框架层怎么兜底。7.1 失败重试机制pytest 中可以通过pytest-rerunfailures插件实现用例失败自动重跑。对于偶发性的网络波动、渲染超时类问题重试一次往往就能过。pip install pytest-rerunfailures运行方式很简单pytest --reruns 2 --reruns-delay 1这个参数的意思是失败后重跑最多 2 次每次间隔 1 秒。但我要提醒你重试是兜底不是遮羞布。如果一个用例持续重试还是失败说明它本身不稳定需要去修等待条件或定位器而不是一味增加重试次数。建议重试次数不要超过 2 到 3 次否则会掩盖真实问题也会大幅拉长执行时间。7.2 日志与截图配合使用排查自动化用例失败最痛苦的就是只知道失败不知道页面当时什么样。所以日志留痕极其重要。我的习惯是在每个用例的关键步骤都打日志失败时同时记录错误信息和截图路径。举个例子可以封装一个简单的日志工具import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s ) def log_step(step_name): logging.info(f[步骤] {step_name})每个用例的执行过程会变成这样2025-01-15 10:23:01 - INFO - 打开登录页面 2025-01-15 10:23:03 - INFO - 输入用户名 2025-01-15 10:23:04 - INFO - 输入密码 2025-01-15 10:23:05 - INFO - 点击登录按钮 2025-01-15 10:23:07 - INFO - 断言登录成功配合失败截图你基本可以在几分钟内定位是页面 Bug、元素调整还是脚本本身的问题。7.3 执行策略用例分级和并行用例不能不分轻重地一股脑全跑。我通常会把用例分成三个级别P0 冒烟级核心主流程每次提交代码必跑要求几分钟内跑完P1 回归级所有核心功能模块发版或 nightly 构建时跑P2 边缘级异常场景、边界值每周跑一次执行顺序上 P0 优先如果 P0 挂了构建直接失败后面的用例不跑节省时间。并行方面pytest 可以通过pytest-xdist实现多进程执行pip install pytest-xdist pytest -n 4注意并行时浏览器进程会同时打开多个内存压力比较大建议控制并发数量观察本机或 CI 容器的负载。等用例数量上去了再引入 Selenium Grid 或云测平台做真正的分布式调度。8. 与热门生态的衔接Pytest 之外的选项和接口测试的关系很多搜索词里会有selenium java接口自动化测试框架自动化测试框架pytest这些词说明大家其实关心两件事Selenium 怎么融入更大的测试体系以及 Java 和 Python 生态到底怎么选。8.1 Java 还是 Python不是一个哪个好的问题Selenium 官方支持的编程语言很多但业界用得最多的还是 Java 和 Python。两者选型可以按团队情况来维度PythonJava上手难度低语法简洁中需要理解面向对象生态pytest/unittest数据驱动方便TestNG/JUnit与 Spring 等集成好招聘供给测试开发岗位很多要求 Python传统企业团队 Java 存量多执行效率解释型略慢编译型性能更好典型场景快速脚本、数据驱动、AI 辅助大型企业级测试平台我在 Python 和 Java 项目里都写过 Selenium 脚本。如果你的团队后端本来就是 Java自动化框架用 Java 会更好跟 CI/CD 平台打通如果是从零开始搭测试体系Python 绝对是最快的路径。8.2 Selenium 与接口自动化测试的关系有些同学会把 UI 自动化和接口自动化对立起来其实不对。两者的定位完全不同接口自动化验证服务端逻辑、数据返回格式、异常处理执行快是 CI 流水线的主力UI 自动化验证前端交互、用户流程、渲染结果覆盖的是用户实际用起来的体验是最后一层防线一个合理的金字塔策略是底层大量接口自动化用例中层核心业务场景用例上层少量 UI 冒烟测试。比如登录功能接口层可以验证各种参数组合的安全性UI 层只需验证用户输入正确账号密码能否进入首页这个真实用户体验链路。如果你只做接口测试前端 JS 错误、按钮不可点、页面跳转错乱这类问题完全发现不了只做 UI 测试执行成本和维护成本又会高得吓人。8.3 Selenium 与 VBA 的场景搜索词里还有 selenium vba 这样的组合。一句话解释VBA 可以通过 InternetExplorer 对象操作浏览器但和 Selenium 完全是两条路线。Selenium 的优势是标准化、跨浏览器、支持完整的自动化测试框架体系VBA 则局限在 Excel/Office 宏场景下的轻量操作。如果只是想在 Excel 里自动抓取网页数据VBA 凑合能用但如果要构建一套正经的 Web 自动化测试体系Selenium 才是该投入的地方。对于数据抓取类需求Python Selenium 的灵活性也远高于 VBA。9. 我压箱底的几条实操经验写 Selenium 这些年有几条经验是踩了不少坑才总结出来的放在最后分享给读者。第一条永远不要在脚本里直接写死等待时间。哪怕你当时测试发现 3 秒稳定通过也不能保证在弱网环境、高负载环境或者浏览器升级后还稳定。显式等待条件不满足就报错而不是靠运气睡觉。多数稳定性问题最终修复方式都是把 sleep 改成 WebDriverWait。第二条元素定位器写得好不好直接决定框架的维护成本。我的建议是能用 ID 不用 class能用相对 XPath 不用绝对 XPath能用关系定位不用层级定位。什么叫关系定位比如某个按钮对应的 label 是提交订单你可以从文本定位到 span再通过它的父节点找到对应按钮。这种定位器对页面微小变化不敏感抗重构能力很强。第三条测试数据要保持独立。不要在用例里创建完测试数据后不清理也不要所有用例共用同一份数据。流程类用例最容易出现的连锁反应是第一条用例创建了订单第二条用例直接依赖这个订单号一旦第一条失败后面全部失败。正确的做法是要么每个用例自己创建独立数据要么在 fixture 中统一准备测试基态。第四条别忽视浏览器升级带来的兼容问题。Chrome 每个大版本发布后ChromeDriver 都要对应更新。如果 CI 环境里一直固定驱动版本某天浏览器自动升级了测试就可能大面积失败。使用 webdriver-manager 或定时更新驱动可以降低这类风险。第五条UI 自动化用例的数量要克制。每个 UI 用例的运行成本都不低包括浏览器启动时间、页面渲染时间、网络往返时间。我经手的项目中最实用的往往是 100 到 300 条精心设计的核心流程用例而不是上千条什么都测的流水账用例。保持用例的干净和有价值比追求数量重要得多。Selenium 这套框架本身并不复杂复杂的是构建在它之上的测试设计、稳定性保障和维护策略。希望这篇内容能帮你少走一些弯路真正把 UI 自动化测试跑出价值。
返回列表