news 2026/10/1 16:54:30

Selenium Web自动化测试实战:从环境搭建到框架落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Selenium Web自动化测试实战:从环境搭建到框架落地

很长一段时间里,我面试测试开发岗位时,总会抛出一个问题:“你写自动化脚本,第一个用例是跑通就收工,还是会继续想页面元素为什么这样定位、等待为什么这样写?”十个人里有八个倒在第二问上。这其实也是很多初学者卡壳的原因:Selenium的API就那么几个,find_element、click、send_keys,背三天就能唬人,但真正扔给你一个动态渲染的前端项目,脚本跑不过十分钟就碎一地。所以这篇不打算只贴一堆“Selenium入门语法”,而是从一次完整的Web自动化测试实战出发,把从环境搭建、脚本设计、框架集成到问题排查的完整链路拆开讲,重点讲清楚每一步背后的取舍逻辑。目标读者是已经会一点Python、想系统掌握自动化测试但还没完整落地过项目的朋友,这篇的内容够你直接参考复现,也能帮你避开我踩过的那堆坑。

1. 自动化测试的方案设计与技术栈选型

1.1 为什么选Selenium而不是其他工具

聊Web自动化,绕不开工具选型。市面上能用的东西其实不少:Playwright、Cypress、Appium(移动端)、Robot Framework这类关键字驱动框架,还有各路国产平台。但我个人做PC端Web功能回归、UI稳定性验证,尤其是需要兼容老系统的场景,最顺手的仍然是Selenium。

原因有三:

第一,Selenium的兼容性覆盖面是最广的。WebDriver是W3C标准,各主流浏览器都原生支持,这意味着同一个脚本可以无缝跑在Chrome、Firefox、Edge上,不用针对浏览器单独改逻辑。Playwright虽然也支持多浏览器,但它的浏览器补丁管理在某些内网环境——尤其是不允许随便下载二进制文件的企业环境——会非常难受,Selenium直接调本地浏览器就能规避这个问题。

第二,生态太成熟了。Selenium从2010年左右火起来,到现在十多年,社区沉淀了大量踩坑文档、驱动管理工具、框架集成案例。遇到问题一搜基本都有答案。这种“前人填坑”的积累程度,直接决定了你上手的顺畅度。Playwright确实更现代,但相对年轻,我在生产环境遇到的一些边缘问题,社区答案确实不如Selenium丰富。

第三,团队协作成本。Selenium配合pytest或TestNG、再配一个Allure报告,团队里任何人都能快速理解脚本结构。而很多封装过度的平台框架,写脚本的人爽了,接手的人能骂街。Selenium的可读性和直白程度,天然适合团队协作。

当然,如果项目是纯新项目、团队没有历史包袱,我也会认真考虑Playwright——它的自动等待机制和拦截网络请求的能力确实做得比Selenium好。但如果你是想系统打基础,Selenium一定是第一站,因为它把“浏览器自动化”的底层逻辑暴露得最清楚:驱动、浏览器、元素、等待,一整套概念搞明白之后,用Playwright、Appium都是降维打击。

1.2 核心工具链:Python、Selenium、pytest的角色分工

这次实战用到的主要组件就四个:Python、Selenium WebDriver、pytest、还有一根WebDriver Manager。各司其职:

  • Python:胶水语言,负责整个测试脚本的逻辑编排。选择Python而不是Java,主要是因为语法简洁、库丰富,写UI测试用例时不需要啰嗦的样板代码。会一点Java的用TestNG效果也不差,但对大多数团队来说,Python的招募成本和上手曲线都更友好。
  • Selenium WebDriver:负责和浏览器对话。它本质上是暴露了一组HTTP接口的服务,Python脚本通过这些接口向浏览器发指令——“打开这个URL”“找到这个元素”“点击它”“输入这段文字”。
  • pytest:测试执行框架。Selenium解决的是“如何操作浏览器”,但“哪些用例要跑”“跑完结果怎么收集”“失败要不要重试”这些测试管理问题,得靠pytest来管。pytest的fixture机制还能优雅地处理setup/teardown——每个用例执行前打开浏览器、执行完关闭,这是原生Selenium脚本很难优雅解决的。
  • WebDriver Manager:自动下载并匹配浏览器版本的驱动文件。没有它,你得手动去找ChromeDriver、GeckoDriver,还得操心浏览器升级后驱动失效的问题。

这几者组合起来的流程大致是:pytest把测试用例收集起来,通过fixture初始化WebDriver实例,WebDriver去操控真实浏览器执行你在用例里写的操作步骤,操作过程中捕获页面状态用作断言,最终pytest汇总测试结果生成报告。

1.3 项目结构和测试场景准备

开始写代码之前,建议先想清楚测试对象。这篇实战我用的演示场景是一个典型的后台管理系统——包含登录页、数据列表页和表单提交页,这是Web项目最高频的三种页面形态。登录页涉及输入框操作和按钮点击,列表页涉及等待异步数据加载和表格元素定位,表单页涉及select下拉、日期控件、文件上传这类特殊交互,覆盖了日常自动化测试的大部分典型痛点。

项目目录我建议按这样的结构组织:

web_auto_test/ ├── conftest.py # pytest fixture 全局配置,管理浏览器实例 ├── pages/ # 页面对象层,每个页面一个类 │ ├── __init__.py │ ├── login_page.py │ ├── list_page.py │ └── form_page.py ├── testcases/ # 测试用例层,只写断言和用例编排 │ ├── __init__.py │ ├── test_login.py │ ├── test_list.py │ └── test_form.py ├── data/ # 测试数据文件,可用yaml/json/excel │ ├── test_data.json │ └── login_users.yaml ├── reports/ # 测试报告输出目录 └── requirements.txt

这种分层结构叫作Page Object Model(POM),是Selenium自动化测试领域最主流的架构模式。核心思想就一句话:把“页面元素定位”和“业务操作”封装到独立的Page类里,测试用例只负责描述业务场景和断言结果,不直接碰“怎么找元素”的细节。好处是当前端改了元素id或class时,只需要改Page类里对应的那一个定义,所有断言这个页面的用例自动修复,不用满工程去搜索替换,维护成本大幅下降。

2. Selenium核心机制深度拆解

2.1 WebDriver的工作原理与浏览器驱动管理

很多新手把WebDriver当成一种“Python库”,其实它是独立的服务进程。完整的工作链路是这样的:你的Python脚本调用Selenium的API,Selenium的客户端库把这次调用封装成一个HTTP请求,发送给WebDriver可执行文件启动的服务,WebDriver再去调用浏览器原生的自动化接口,驱动浏览器执行真实操作。

这里有个关键点:WebDriver的版本必须和浏览器版本严格匹配。Chrome 120对应ChromeDriver 120.x,Firefox对应GeckoDriver,版本对不上,启动浏览器时会直接抛SessionNotCreatedException。

手动管理驱动确实麻烦,所以我后面全部改用webdriver-manager这个库,它会根据你的浏览器版本自动下载对应驱动并缓存到本地:

from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager def create_browser(): service = Service(ChromeDriverManager().install()) options = webdriver.ChromeOptions() options.add_argument("--start-maximized") # 如需要无界面模式,取消下面注释 # options.add_argument("--headless") driver = webdriver.Chrome(service=service, options=options) return driver

首次运行时会看到webdriver-manager去下载驱动的日志,之后会走本地缓存,基本几秒钟就能启动。这套方案在企业内网环境下可能因为网络限制卡住,但大多数情况下比自己手动维护驱动要省心太多。

2.2 元素定位:八种策略的选用原则

Selenium提供八种元素定位方式:id、name、class_name、tag_name、css_selector、xpath、link_text、partial_link_text。实际项目里用得最频繁的是id、css_selector和xpath这几种。

我个人的选用原则,按优先级排:

  1. id优先。id在HTML规范里要求页面内唯一,定位速度快、代码简洁,后端管理系统里的关键元素大多会带上id。比如登录按钮写成<button id="loginBtn">,直接driver.find_element(By.ID, "loginBtn")就够了。
  2. css_selector次之。现代前端框架(Vue、React)生成的页面里id不一定是稳定的,但class往往是语义化的。css_selector写法紧凑、性能好,支持复杂的层级关系。
  3. xpath兜底。xpath是万金油,页面结构特别混乱、没有合适的class时,用xpath的文本定位或轴定位通常能解出来。但xpath性能相对差,语法也容易写出一长串屎山一样的表达式,能用css尽量不用xpath。

举个例子,需要定位一个表格里的“操作”列下的“编辑”按钮,css写法可能是:

# 找到包含文本"编辑"的按钮,且它位于id为dataTable的表格区域内 driver.find_element(By.CSS_SELECTOR, "#dataTable .btn-edit")

如果页面结构不稳定,用xpath的文本匹配更稳:

driver.find_element(By.XPATH, "//button[contains(text(), '编辑')]")

这里说一个很多新手会踩的坑:定位表达式不要写得太绝对。比如//*[@id="app"]/div/div[2]/div[1]/div[3]/button这种“绝对路径”,前端稍微加一层div就全盘崩溃。更稳的做法是先找不变的外层容器,再在容器内用相对关系定位。还有,能用By.ID就少用By.XPATH,别为了耍帅硬写长表达式。

2.3 显式等待、隐式等待与强制休眠的正确姿势

Selenium脚本最常见的不稳定因素,就是页面元素还没加载出来就开始操作,导致NoSuchElementException。不少人图省事直接sleep(3),先不管精确性,这也能跑。但遇上网络波动,3秒不够就翻车,遇上秒开页面又白白等3秒,测试总时长被拖得很长。

WebDriver提供了两套正经的等待机制:

隐式等待(implicitly_wait)设置后,全局所有find_element操作会在元素没找到时轮询等待一段时间再报错:

driver.implicitly_wait(10) # 最多等10秒

显式等待则是针对特定元素的条件等待,用WebDriverWait配合expected_conditions:

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "dataTable")) )

两者可以共存,但要小心隐式等待和显式等待叠加时,最坏等待时间会膨胀到两者的总和。所以我的习惯是:全局只设一个较短的隐式等待(比如5秒),对关键异步元素用显式等待精确控制。

关于等待条件的选择,presence_of_element_located只表示元素存在于DOM树,不代表可见可点击。所以点按钮之前,更严谨的条件是element_to_be_clickable;输入框需要可见时用visibility_of_element_located。这个区别在实际项目里直接决定了脚本命不命中最刁钻的bug。

3. 从零实现自动化脚本的完整实操

3.1 环境准备:安装Python与配置虚拟环境

如果你机器上还没装Python,先去python.org下载对应系统的安装包。这一步有个细节:Windows安装时记得在引导页面勾选“Add Python to PATH”,不然装完了命令行里敲python没反应,会让你怀疑人生。装完验证一下:

python --version

然后为这个项目创建独立的虚拟环境,避免依赖污染系统Python:

mkdir web_auto_test && cd web_auto_test python -m venv venv # Windows激活虚拟环境 venv\Scripts\activate # macOS/Linux激活虚拟环境 source venv/bin/activate

创建虚拟环境这个步骤我强烈建议不要省——你后面同时搞爬虫、数据分析、Web开发时就知道依赖隔离有多重要了。然后安装依赖:

pip install selenium pytest webdriver-manager

把依赖版本固化下来:

pip freeze > requirements.txt

这样换机器或同事接手时,pip install -r requirements.txt一步搞定。

3.2 第一个冒烟脚本:打开浏览器并验证页面标题

环境准备好后,先从最简单的冒烟用例开始,验证Selenium能够成功驱动浏览器。新建一个临时文件smoke_test.py:

from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) try: driver.get("https://example.com") assert "Example Domain" in driver.title print("冒烟测试通过,页面标题:", driver.title) finally: driver.quit()

跑一下看效果:

python smoke_test.py

如果看到Chrome窗口自动打开、跳转页面、然后打印出标题并关闭,恭喜,你的自动化环境已经通了。这一步虽然简单,但一次性打通了“驱动下载→浏览器启动→页面访问→断言→资源回收”的完整链路,后续所有脚本都是在这个基础上长出来的。

3.3 登录用例实战:输入框、按钮与流程断言

拿一个模拟的后台登录页来演示(你可以在本地起一个简单的HTML服务,或者用测试环境)。先写Page类,把登录页的元素和操作封装起来:

# pages/login_page.py from selenium.webdriver.common.by import By class LoginPage: def __init__(self, driver): self.driver = driver # 元素定位集中管理,前端改动只改这里 _username_input = (By.ID, "username") _password_input = (By.ID, "password") _login_button = (By.ID, "loginBtn") _error_tip = (By.CLASS_NAME, "error-message") def input_username(self, username): self.driver.find_element(*self._username_input).clear() self.driver.find_element(*self._username_input).send_keys(username) def input_password(self, password): self.driver.find_element(*self._password_input).clear() 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 get_error_tip(self): return self.driver.find_element(*self._error_tip).text

注意两个实操细节:第一个是输入前先.clear()。如果上一次用例失败导致输入框里残留了值,不清空直接send_keys会把新旧内容拼在一起,而且这个bug很难排查。第二个是把元素定义成类属性的元组,调用时用*解包。这是Selenium社区比较通行的写法——元素定位和操作分离,维护时只看顶部那几行定位即可。

然后写登录场景的测试用例:

# testcases/test_login.py from pages.login_page import LoginPage def test_login_success(init_driver): driver = init_driver driver.get("http://your-test-site.com/login") login_page = LoginPage(driver) login_page.login("admin", "123456") # 断言登录成功后跳转到首页,且页面出现欢迎语 assert "欢迎" in driver.page_source

这里init_driver是conftest.py里定义的一个fixture,负责创建浏览器、用例结束后关闭浏览器。

3.4 复杂交互:下拉选择、iframe、弹出框与文件上传

实际业务系统没这么乖巧,总有几个页面爱用特殊控件。这里集中说一下四种高频特殊交互。

下拉框。原生<select>元素可以用Selenium封装的Select类:

from selenium.webdriver.support.ui import Select select_element = driver.find_element(By.ID, "category") Select(select_element).select_by_visible_text("电子设备")

但如果前端用了第三方组件库(比如Element UI、Ant Design的定制下拉),页面上实际渲染的不是原生select,而是div模拟的列表,这时候Select类就不管用了,得改为:点击下拉框容器→等待列表出现→点击目标选项文本。

iframe。有些页面把富文本编辑器或第三方地图嵌在iframe里,直接用外层driver找里面的元素是找不到的,必须切进去:

driver.switch_to.frame(driver.find_element(By.ID, "editorFrame")) # 操作iframe内部的元素 ... # 操作完记得切回主文档 driver.switch_to.default_content()

网页弹窗。分成操作系统原生alert和页面自定义modal两种。原生alert用driver.switch_to.alert.accept()处理;页面modal本质是普通div,按普通元素定位即可,但要注意等待动画结束再点击。

文件上传。input标签的文件框可以直接send_keys传本地路径,这是最坑也最简单的方式:

driver.find_element(By.ID, "uploadFile").send_keys("/path/to/local/file.pdf")

不需要去模拟点击系统的文件选择窗口,那个反而是自动化的大坑。

3.5 等待策略与防抖处理

前端路由跳转后,新页面的数据往往是异步接口加载的。如果接口返回慢,脚本立即去找表格里的数据,极大概率扑空。我常用的终极等待策略是“等待目标元素可见,且元素文本符合预期”:

WebDriverWait(driver, 10).until( lambda d: d.find_element(By.ID, "dataTable").text != "" )

这个lambda写法轮询检查表格内容非空,比EC.presence_of_element_located更贴近业务场景——元素一直在,但内容是异步加载的,等到空文本变成有内容才算真正“加载完成”。

另一个容易忽略的“防抖”场景是:点击“保存”按钮后,前端会先提交请求,可能还会弹出一个loading遮罩,保存完成后遮罩消失。如果保存后马上断言某个表单消息弹出,可能会撞上loading遮罩还没收起来的窗口期。稳妥做法是显式等待遮罩不可见:

WebDriverWait(driver, 10).until( EC.invisibility_of_element_located((By.CLASS_NAME, "loading-mask")) )

这类时序问题,是UI自动化里最隐蔽也最消磨耐心的。我的经验是:只要脚本出现“偶发失败”,先怀疑等待条件不对,而不是先怀疑定位错了。

4. 用pytest构建可维护的自动化测试框架

4.1 conftest.py中fixture的设计

接着正式落地框架。第一步把浏览器初始化和回收逻辑收敛到conftest.py的fixture里。我维护一个init_driver:

# conftest.py import pytest from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager @pytest.fixture def init_driver(): service = Service(ChromeDriverManager().install()) options = webdriver.ChromeOptions() options.add_argument("--start-maximized") driver = webdriver.Chrome(service=service, options=options) driver.implicitly_wait(5) yield driver driver.quit()

yield取代return是实现“用例后清理”的关键。yield前的代码是setup——每个用例执行前创建浏览器并交给用例使用;yield后的代码是teardown——用例结束后无论是成功还是失败都会关闭浏览器。这比在用例里try-finally手动清理要优雅得多。

如果你的测试数据需要区分环境,还可以写一个fixture从yaml或json读配置:

@pytest.fixture def base_url(): return "http://your-test-site.com" # 可以改成从环境变量或配置文件读取

4.2 数据驱动:把测试数据从脚本里剥离开

登录用例往往要覆盖多种组合:正确账号、错误密码、空用户名、账号不存在等等。如果每个组合都写一个测试函数,代码会爆炸,而且测试数据和脚本逻辑搅在一起,后续维护很痛苦。

pytest的parametrize装饰器是解决这个问题的标准姿势:

import pytest @pytest.mark.parametrize("username,password,expected_tip", [ ("", "123456", "用户名不能为空"), ("admin", "wrong", "用户名或密码错误"), ("admin", "123456", None), # 成功的用例,expected_tip为None ]) def test_login_cases(init_driver, base_url, username, password, expected_tip): driver = init_driver driver.get(base_url + "/login") login_page = LoginPage(driver) login_page.login(username, password) if expected_tip: assert login_page.get_error_tip() == expected_tip else: assert "欢迎" in driver.page_source

这样一组数据就跑出三条用例,报告里会分别展示,出了错也能一眼定位是哪组数据的问题。当用例数量多了以后,可以把数据挪到外部的yaml或json文件里,用fixture读取后按参数循环传入,这就是完整的数据驱动了。

4.3 用例运行与报告生成

框架搭好后,执行测试:

pytest testcases -v --tb=short

-v显示每个用例的详细结果,--tb=short控制失败时回溯信息的长度,不至于刷屏。想要输出HTML报告,安装pytest-html:

pip install pytest-html pytest testcases --html=reports/report.html --self-contained-html

生成的报告是自包含的HTML文件,可以直接发给团队成员看,里面会展示每条用例的执行状态、失败原因、耗时等信息。如果你想拥有更精美的Allure报告体系,搭配allure-pytest也能达到,但那是另一个大话题了。

我的建议是:报告能看“成功/失败/失败原因/耗时”就够用了。很多团队把大量精力花在定制报告上面,其实UI自动化最大的价值是帮你发现回归问题,而不是比谁的报告好看。

4.4 运行策略与CI集成

本地调试阶段,建议用-k参数精准筛选用例:

pytest testcases/test_login.py -k "success or error" -v

全量回归时可以按模块并行执行。需要装pytest-xdist:

pip install pytest-xdist pytest testcases -n 4 # 4个进程并行

并行执行会大幅缩短回归时间,但要注意多个浏览器实例同时跑同一套数据时,如果被测系统没有做并发隔离,可能出现数据冲突。偶发性的脏数据比慢几秒更头疼,所以并行之前先确认测试数据各用例间互不影响。

CI集成方面,我踩过的正经套路是把自动化任务挂到Jenkins或GitLab CI上,提交代码后自动触发测试。这里有两个关键点:CI环境通常没有显示器,浏览器要开headless模式;CI环境需要预装浏览器本体,WebDriver Manager只能装驱动,不能装浏览器。

options.add_argument("--headless") options.add_argument("--no-sandbox") options.add_argument("--disable-dev-shm-usage")

这三个参数是Linux无头环境的标配。--no-sandbox和--disable-dev-shm-usage是为了规避容器环境资源受限时Chrome崩溃的问题,不加的话在Docker里跑非常容易一脸黑线。

5. 实战中的典型问题与排查实录

5.1 元素定位失败:NoSuchElementException

这大概是新手遇到最多的异常。排查顺序我从高到低排:

先看元素是不是在iframe里。用Chrome开发者工具选中元素,看Element面板里有没有显示“iframe”祖先节点。有的话先用switch_to.frame切进去。

再看是不是页面没加载完。隐式等待时间设短了,或者异步内容还没渲染。先打开DevTools的Network面板确认接口返回状态,再用显式等待增加缓冲。

再看定位表达式是不是写死了。前端重构改了class名或id,检查一下最新的DOM结构。

最后看元素是不是有多个匹配。find_element永远返回第一个匹配元素,如果目标其实在第2个位置,你就控制错了对象。必要时用find_elements先取列表、按下标取值。

5.2 元素被遮挡无法点击:ElementClickInterceptedException

这个异常字面意思很直白:目标元素在DOM中存在,但被其他元素挡住了,浏览器拒绝点击。常见原因有三个:

一是弹窗遮罩层没关闭或还在动画中。处理办法是等待遮罩不可见,或者按一下Esc键试试。

二是页面有fixed定位的悬浮元素(比如顶部推介条、客服浮窗)盖住了按钮。这种在DevTools里很容易发现,处理方式是先用execute_script滚动到元素位置再点击,或者用JavaScript的click()绕过拦截:

element = driver.find_element(By.ID, "submitBtn") driver.execute_script("arguments[0].click();", element)

这是非常实用的“曲线救国”方案,但它绕过了真实用户的点击路径,所以只在确认页面交互逻辑无碍的情况下使用。

三是元素本身的disabled属性。这时候直接点击就会报拦截异常。需要等它变成可点击再用element_to_be_clickable条件去等。

5.3 脚本不稳定:偶发失败的排查思路

脚本“时好时坏”是最消耗斗志的状态。我有一套固定的复盘流程:

先把失败现场留下来。浏览器截图和HTML快照是两个黄金证据:

# conftest.py 里挂一个失败截图钩子 import os import pytest @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield report = outcome.get_result() if report.when == "call" and report.failed: driver = item.funcargs.get("init_driver") if driver: os.makedirs("reports/screenshots", exist_ok=True) driver.save_screenshot(f"reports/screenshots/{item.name}.png")

再看失败的时间点:是统一集中在某一时刻,还是随机分布。集中在同一时刻,大概率是后端服务有定时任务或缓存刷新导致数据变化;随机分布,优先怀疑等待条件不够健壮。

再看失败的用例归属:是不是都集中在同一个页面模块。如果是,那个页面很可能用了你没见过的交互组件,需要重新走一遍手工操作流程,记录下每一步的DOM状态。

最后看数据影响:用例是否依赖某些共享测试数据,比如登录用同一个账号,如果前一个用例把这个账号挤下线了,后面的用例必挂。解决方案是用独立测试账号,或每次登录前重置会话状态。

5.4 浏览器驱动版本不匹配的坑

Chrome浏览器会自动更新,尤其默认开启自动更新的Windows和macOS,今天还是120,明天醒来可能就变121了,而本地缓存的ChromeDriver还是120的,启动时直接报:

SessionNotCreatedException: This version of ChromeDriver only supports Chrome version 120

解决方案有两个方向。第一,每次跑测试前主动检查驱动版本,webdriver-manager在启动时会比对浏览器版本,发现不匹配会重新下载。但它的判断逻辑有时不够即时,稳妥做法是定期手动清一次驱动缓存:

# 清除webdriver-manager的缓存(路径因系统而异) rm -rf ~/.wdm/drivers/chromedriver

第二,把浏览器的自动更新关掉,用固定的企业版本配合锁定版本的驱动。这在测试环境可控的场景下是性价比很高的方案——稳定压倒一切,新特性让开发环境去踩。

5.5 无效定位器与等待时间过长的取舍

有个操作习惯我特别推荐:一个用例脚本写完后,先把隐式等待全部改掉,换成显式等待,跑一遍看稳定性;再把所有显式等待放到独立的“等待工具函数”里统一管理。这样调试时只需要改一个地方,不需要翻遍整个项目找那几行裸等待。时间长了,你会形成一套自己的“等待方法论”:界面操作前等可点击,数据加载等文本内容变化,页面跳转等URL或标题变化。

等待时间也不是越长越好。默认10秒已经是比较宽裕的,如果某个操作需要超过10秒才能完成,先怀疑是不是接口性能有问题,而不是急着把超时时间调到30秒——那只是在掩盖问题。

6. 最后的实操心得与进阶建议

自动化测试这条路,入门不难,难的是“稳定”和“可维护”。我见过太多项目的自动化脚本,刚写出来能跑,一个版本迭代后就碎成一地,最后不了了之。核心问题通常不在代码本身,而在架构和习惯。

给刚上手的朋友几条实实在在的建议,都是我反复踩坑后的体会:

写自动化测试前,先手工把核心业务流程完整走一遍。只有在手工操作时记录了每一步的页面状态、等待时长、异常提示,你才可能写出靠谱的定位条件和等待策略。直接照着需求文档闷头写脚本,大概率会被现实教育。

等待优先用显式等待,不要依赖强制休眠。但也不要走极端——个别场景下,比如页面有ESLint报错导致控制台一直转圈,强制等1到2秒反而比纠结等待条件更高效。工具是死的,人是活的。

元素定位信息不要散落在用例各处。统一收敛到Page类里,或者至少收敛到一个locators模块。前端改动是常态,把改动影响控制在一个文件里,是自动化测试能活过三个迭代的前提。

数据准备和清理必须提上日程。自动化测试跑得勤了,会在被测系统里积累大量测试数据。你得有一个机制在测试结束后清理数据,或在测试开始前用独立账号隔离数据。不解决数据问题,脚本本身再稳定也会被脏数据拖垮。

不要把断言写得过于细致。有些测试人员恨不得一个用例里断言十几次页面文本,结果是前端任何文案微调都会导致用例失败,而这些失败毫无价值。断言应该聚焦在业务核心状态上——登录是否成功、数据是否正确提交、状态是否变更,细枝末节的文案变化不值得成为回归噪声。

等到你把Selenium这套体系摸透了,下一步建议去了解一下Playwright的自动等待与网络拦截机制,对比着用会很有启发;再往深走,可以把Requests直接调接口做数据准备、再用Selenium验证界面展示,接口加UI双层配合,自动化覆盖率会大幅提升。UI自动化不是万能的,但掌握它,你的测试工具箱会完整一大截。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 16:52:59

为什么 Java 泛型这么聪明,结果运行时却像什么都不知道?

全文目录&#xff1a;开篇语前言一、泛型到底是干嘛的&#xff1f;——编译器的“贴心保姆”二、类型擦除&#xff08;Type Erasure&#xff09;&#xff1a;所谓“运行时失忆”是怎么搞出来的&#xff1f;1. 擦除的基本思想2. 擦除的具体步骤&#xff08;简化理解版&#xff0…

作者头像 李华
网站建设 2026/10/1 16:52:56

一个快捷键跑通离线语音转文字:Handy 本地语音识别上手指南

一个快捷键跑通离线语音转文字&#xff1a;Handy 本地语音识别上手指南 【免费下载链接】Handy A free, open source, and extensible speech-to-text application that works completely offline. 项目地址: https://gitcode.com/GitHub_Trending/handy11/Handy Handy …

作者头像 李华
网站建设 2026/10/1 16:52:54

RomM 固件摆放:目录与文件名配对,GBA 游戏不再黑屏

RomM 固件摆放&#xff1a;目录与文件名配对&#xff0c;GBA 游戏不再黑屏 【免费下载链接】romm A beautiful, powerful, self-hosted ROM manager and player. 项目地址: https://gitcode.com/GitHub_Trending/rom/romm 点开一台 GBA 游戏&#xff0c;画面卡在开机前的…

作者头像 李华
网站建设 2026/10/1 16:51:10

C++享元模式实战:从内存爆炸到内存减半,附完整代码与压测

爆内存那次&#xff0c;我才真正吃透C的享元模式。当时在做一个地图编辑器&#xff0c;单张地图要刷几万个树木贴图&#xff0c;第一版直接new对象&#xff0c;程序跑到一半内存像喝水一样涨&#xff0c;CPU也卡成幻灯。后来把贴图资源抽出来共享&#xff0c;同样的场景内存直接…

作者头像 李华
网站建设 2026/10/1 16:50:37

CMake变量管理实战:从CMA_cma_命名到缓存与工具链避坑

简介&#xff1a;这份资源聚焦光纤通信中的偏振模色散补偿问题&#xff0c;面向从事光通信系统仿真、数字信号处理算法研究的学生与工程师。内容围绕CMA算法的完整流程展开&#xff0c;涵盖数据预处理、PMD参数估计、补偿矩阵计算、信号恢复、迭代优化及性能评估等关键环节&…

作者头像 李华