资讯动态

Selenium Web自动化测试实战:从稳定定位到框架设计

发布时间:2026/10/17 3:45:47 来源:尧图企业网站定制
先说个结论Selenium本身并不难难的是让用例在一周、一个月之后还能稳定地跑下去。我见过太多人第一天写完十个用例信心满满第二天跑挂一半第三天开始怀疑是不是框架选错了。其实问题往往不是工具而是你对待元素定位、等待时机、用例组织这些基本功的方式。这篇文章我想从自己多年的实际使用经验出发把Selenium做Web自动化测试的完整路径拆开讲一遍——包括为什么选它、环境怎么搭才不折腾、定位器和等待怎么做才稳、测试代码怎么组织才扛得住版本迭代以及那些真正让我熬夜排查的经典问题。如果你正在入门Selenium或者已经写了一段时间脚本但总觉得维护成本高得离谱这篇文章应该能给你一些参考答案。1. 为什么我最终选定Selenium做Web自动化——选型背后的真实考量1.1 自动化方案对比不是只有Selenium一种选择很多新手以为Web自动化就等于Selenium实际上现在可选方案已经非常多了比如Playwright、Cypress、Puppeteer它们在某些场景下甚至比Selenium更顺手。我自己的项目里也试过Playwright它的自动等待、网络拦截、多上下文支持确实很惊艳适合新项目从零开始建Cypress在纯前端项目里的调试体验也很好安装简单语法友好。但为什么我在大部分长期维护的项目里还是用Selenium核心原因是三个字不折腾。先说兼容性。Selenium支持的浏览器覆盖范围最广主流的Chrome、Edge、Firefox、Safari都有对应的Driver支持而且能直接操作真实的浏览器实例不是模拟环境。有些内部系统只兼容某个旧版浏览器这种情况下Playwright和Cypress往往会让你很痛苦Selenium则基本没有边界限制。再说语言。Selenium官方支持Java、Python、C#、Ruby、JavaScript、Kotlin等多种语言这意味着测试团队可以用自己最擅长的语言来写现有代码资产可以平滑迁移而不是强迫所有人学会一门新工具。还有一点很实际团队成员对Selenium的熟悉程度普遍更高。新人入职后看代码、改用例的阻力小社区里能查到的踩坑资料最多遇到奇怪问题搜一下基本都有答案。工具选型这件事从来不是选最好的而是选最合适的。1.2 Selenium 4和WebDriver的架构变化如果你是从Selenium 3时代过来的会明显感觉到Selenium 4在架构上干净了很多。它全面拥抱了W3C WebDriver标准不再像以前那样通过JSON Wire Protocol做额外的编码转换而是浏览器驱动直接和浏览器通信。带来的实际好处是不同浏览器之间的行为差异更小了脚本的兼容性更好某些奇怪的这个浏览器能过、那个浏览器挂了的问题明显减少。Selenium 4里我非常喜欢的新特性是相对定位器Relative Locator。以前我想找某个按钮上方的输入框这种位置关系的元素只能自己去写XPath写出来又长又脆。现在可以直接用to_left_of、to_right_of、above、below这些方法代码读起来就像在描述需求非常直观。另外一个常用改动是窗口管理API以前切换新标签页要自己做switch_to.window(window_handles[-1])现在有了switch_to.new_window(tab)流程简化了不少。这些变化表面上看只是API调整实质上是整个工具链在向标准化、可预测迈进。对于测试脚本来说越少需要为浏览器差异打补丁维护成本就越低。1.3 什么样的项目适合用Selenium做自动化说实话Selenium不是万能药有些项目用它做自动化就是在给自己挖坑。我一般会在写第一行脚本之前先做一轮评估如果符合下面大部分条件才建议用Selenium上业务核心是通过浏览器操作的Web系统且回归测试频率很高页面元素有相对稳定的定位特征ID、name、data属性或者前端会配合测试约定添加可识别属性需要在多个浏览器或同一浏览器的不同版本上验证兼容性团队有足够的耐心维护定位器和用例接受测试脚本也要持续重构这个前提。反过来如果你的系统里大量使用验证码、文件上传弹窗、复杂的Canvas图表、桌面级拖拽操作或者测试团队根本没有人愿意长期维护自动化代码那我劝你别硬上。Selenium能做很多事但它替代不了人肉测试也替代不了产品逻辑的梳理。先把自动化定位成辅助回归、发现问题再逐步扩大覆盖范围会比一上来就追求全量自动化稳妥得多。2. 环境搭建里那些文档不会明说但绕不开的细节2.1 驱动版本与浏览器版本的那场博弈新手建Selenium环境时大概率撞过这个经典报错session not created: This version of ChromeDriver only supports Chrome version x。原因很简单Chromedriver的版本必须和浏览器版本严格匹配一个版本号对不上整个会话就起不来。而且浏览器经常自动更新今天脚本跑得好好的明天因为浏览器升级就全部罢工的场景我至少遇见过十次。所以我现在很少用手动下载Driver的方式而是直接用第三方依赖库来自动管理驱动版本。Python项目里最常用的是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)第一次运行会自动下载匹配当前浏览器版本的Driver以后每次启动时会自动检测并更新。如果用的是Selenium 4.6及以上版本官方自带了Selenium Manager连webdriver-manager都不用装Selenium会自动处理Driver的下载和匹配方便很多。我之前在某项目里就吃过手动管理Driver的亏测试环境里固定放了一个老版本Driver有一天运维把浏览器升级了所有回归用例瞬间从全绿变成全红而报错信息又很有迷惑性大家排查了很久才意识到是Driver版本问题。从那以后我所有环境的Driver都交给自动化管理再也没手工碰过Driver下载。2.2 在无头模式和图形模式之间切换的正确姿势CI环境里跑测试几乎都会用无头模式headless也就是浏览器不显示界面只在后台执行。这能省不少资源也不占用服务器的桌面会话。但我在实际使用中发现两个容易踩的坑第一个坑是无头模式下被网站识别为非真实浏览器导致某些前端逻辑走不到。原因之一是无头模式下User-Agent里通常带HeadlessChrome字样部分站点会因此调整行为。解决办法是显式指定User-Agentoptions webdriver.ChromeOptions() options.add_argument(--headlessnew) options.add_argument(--window-size1920,1080) options.add_argument(user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36)这里的窗口大小也很重要无头模式下默认窗口尺寸可能只有800x600有些页面在窄屏下会触发响应式布局元素位置、显示状态全变了定位器自然就失灵。我一般会显式设一个1920x1080的窗口尺寸避免这种隐性差异。第二个坑是本机调试和CI环境共用一套配置。我建议把图形模式和无头模式抽成配置项本机调试默认用图形模式CI环境强制无头模式。这样既能在开发时直观看到浏览器交互又不会把本机环境绑定死。headless_mode os.getenv(HEADLESS, false) true if headless_mode: options.add_argument(--headlessnew)2.3 远程执行与本地执行Selenium Grid什么时候值如果你的测试体量不大本地跑就够用。但多浏览器覆盖、并行执行、多台机器共享浏览器环境的需求一上来就得考虑Selenium Grid。Grid允许你把测试分发到多个节点每个节点可以配置不同的浏览器镜像类似Hub负责接收测试请求并调度Node注册到Hub声明自己支持哪些浏览器和版本测试代码通过Remote WebDriver指向Hub地址。我在项目里用过Docker Compose直接起Grid配置一个Hub加三个NodeChrome两个版本、Firefox一个版本测试脚本里设置from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() driver webdriver.Remote( command_executorhttp://localhost:4444/wd/hub, optionsoptions )这样就能在同一套测试代码上同时验证多个浏览器组合。不过Grid带来的运维成本也不低节点崩溃、镜像版本过旧、网络延迟都会影响稳定性。如果团队规模不大我更推荐先用本地并行加无头模式等到用例量真的需要跨浏览器矩阵执行时再引入Grid不要为了看起来专业而提前增加复杂度。3. 元素定位的实战策略从能定位到稳定定位3.1 定位器优先级先问该不该用再问怎么用元素定位是Selenium里最容易被低估的环节。年轻时候我看到什么顺手就用什么有class用class有XPath写很长一串反正当时能跑通。后来当页面改版、前端组件库升级一条用例里十来个定位器轮番失效的时候我才终于整理出一套优先级规则ID页面里应该唯一最稳定优先级最高数据属性比如>submit_button driver.find_element(By.CSS_SELECTOR, button[typesubmit]:not([disabled]))一句话解决状态过滤问题不用写又长又脆的//button[typesubmit and not(disabled)]。3.3 XPath的克制与优化只在需要的时候用XPath不是不能用而是要有节制地使用。当你确实需要用文本内容匹配元素或者需要根据某个元素附近的另一个元素来定位时XPath几乎是唯一选择。但写XPath有几个原则避免从/html/body/div[1]/...这种绝对路径开始页面结构稍微一变就全断多用相对路径配合//定位比如//form[idlogin-form]//input[nameusername]文本匹配尽量用normalize-space()去除多余空格避免contains(text(), )带来的歧义组合条件比单条件更稳//button[contains(class, submit) and not(disabled)]。XPath还有一个常见的反直觉情况浏览器里复制出来的绝对XPath往往非常长但复制出来能跑不代表适合维护。我见过有人粘贴出这样的定位器/html/body/div[3]/div/div[2]/form/div[2]/div/div[1]/button[1]页面只要加一个弹窗前面多一个div整个定位器就废了。克制使用XPath是为了让定位器在一百次页面变更后依然能找到那个元素。3.4 动态页面里的拦路虎iframe、Shadow DOM与延迟渲染动态页面是定位器失效的重灾区。先说iframe很多系统里嵌入了第三方组件、富文本编辑器元素并不在当前文档树里直接find_element就是找不到。这时候要先切进iframeiframe driver.find_element(By.CSS_SELECTOR, iframe[titleeditor]) driver.switch_to.frame(iframe) # 操作完记得切回主文档 driver.switch_to.default_content()切回主文档这个动作特别容易被忘忘了之后下一个定位器就会诡异地失败。再说Shadow DOM这是现代前端组件库比如一些Web Components带来的新问题。Selenium 4默认能穿透Shadow DOM的闭包但需要用shadow_root属性一层一层进入host driver.find_element(By.CSS_SELECTOR, #widget-host) shadow driver.shadow_root button shadow.find_element(By.CSS_SELECTOR, button.confirm)延迟渲染的问题更隐蔽。页面的某个区域要等接口返回后才出现如果你在前面用了全局隐式等待可能时好时坏。真正有效的解决思路是后面会专门讲的显式等待而不是一味加大sleep。定位器的世界里有句老话稳定的脚本不是找得快而是等得准。4. 等待机制是自动化脚本的生命线显式等待的正确用法4.1 三种等待方式对比别再用sleep硬扛等待机制处理得不好脚本就会呈现出一种令人抓狂的薛定谔状态本地跑十次有九次过CI上跑十次塌五次。几乎所有这类问题根源都在等待策略没有设计好。Selenium里常见的等待方式有三种强制等待time.sleep、隐式等待driver.implicitly_wait、显式等待WebDriverWait。我直接说结论强制等待能不用就不用隐式等待最多作为一个全局兜底显式等待才是核心策略。等待方式原理优点风险强制等待sleep固定暂停系统线程用法最简单太快则误报太慢则浪费时间网络波动下时好时坏隐式等待全局设置轮询超时findElement找不到元素时会重试设置一次全局生效对页面加载、元素状态变化不敏感与显式等待混用会拖慢整体速度显式等待对特定元素/条件轮询直到满足条件或超时精确、可控、可自定义需要为关键步骤逐个设置代码略多关于隐式等待和显式等待的配合Python的Selenium官方和社区都建议不要混合使用因为两者底层用的是不同的超时机制混在一起可能出现等待时间叠加让用例无谓地变慢。所以我的习惯是不设全局隐式等待所有关键交互都走显式等待。4.2 WebDriverWait与expected_conditions的最佳实践显式等待的标准模板是from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By wait WebDriverWait(driver, timeout10) login_btn wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, #login-btn)))这里有一个很关键的理念等待的条件要和你接下来要对元素做的操作保持一致。如果你只是要检查元素存在用presence_of_element_located没问题但如果你准备click()那必须等element_to_be_clickable它保证了元素不仅存在而且可见、可点击。很多click偶发性失败的用例归根结底就是等了存在却不等可点击。我自己的流程一般是页面跳转后用presence_of_element_located等待核心区域出现避免页面还没加载完在提交、删除这类关键操作前用element_to_be_clickable等待按钮可用操作后等待某个有业务意义的标识出现比如成功提示、按钮变为已完成而不是随便sleep两秒如果某个请求完成后页面某个元素会自动消失也可以等它消失比如EC.invisibility_of_element_located。这套流程的核心是围绕业务状态等待而不是围绕时间等待。等待时间本质是让页面到达一个可预测的状态这样后面的每一步才有意义。4.3 自定义等待条件解决Ajax与状态刷新的难题有些场景官方提供的expected_conditions不够用。比如表格数据正在刷新你要等待旧数据被替换成新数据或者某个按钮上的文案从提交中变成提交完成。这种时候我会写自定义等待条件def text_to_appear(driver, locator, text): 等待元素内部文本变成指定的内容 def _predicate(driver): element driver.find_element(*locator) return element.text.strip() text return _predicate wait.until(text_to_appear((By.CSS_SELECTOR, #submit-btn), 完成))不要小看这种几个函数它能解决大量Ajax异步更新的场景。如果你发现一个用例总是时好时坏多半就是状态还没有到达就继续往后执行这时候别急着加大sleep而是想想当前步骤完成后页面上的哪个确定性信号会出现或消失。等待那个信号比等待两秒可靠得多。4.4 超时之后的诊断设计截图与页面源码显式等待超时后WebDriverWait会抛出TimeoutException但报错信息通常只告诉你element not found。问题在于页面当时到底是什么状态元素是压根没渲染还是被弹窗挡住了还是iframe没有切进去光靠一条报错日志根本看不出来。所以我会在所有测试的基础入口处做一件事任何异常抛出时立刻捕获当前截图和页面源码保存到当天的报告目录。加一个pytest的fixture很容易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: timestamp time.strftime(%Y%m%d_%H%M%S) driver.save_screenshot(freports/{item.name}_{timestamp}.png) with open(freports/{item.name}_{timestamp}.html, w, encodingutf-8) as f: f.write(driver.page_source)有了截图和页面源码定位等待超时的根因通常只需要一分钟而不是靠猜。5. 我把测试代码组织成框架后维护成本才真正降下来5.1 Page Object模式不是简单封装是给页面建模很多人的自动化代码长这样每个用例里反复写driver.find_element(...)用例越长重复越多。一旦登录按钮的属性变了就要全局搜索五六个地方去改。这种脚本能跑但压根谈不上可维护。我用的是几十年来社区验证过的Page Object模式核心思想是每个页面是一个类页面的元素是类的属性页面的操作是类的方法用例只关心业务流程不关心具体元素长什么样。举个例子登录页的Page Object长这样class LoginPage: def __init__(self, driver): self.driver driver def input_username(self, username): self._wait_for_username().send_keys(username) def input_password(self, password): self._wait_for_password().send_keys(password) def click_login(self): self._wait_for_login_button().click() def login(self, username, password): self.input_username(username) self.input_password(password) self.click_login() def _wait_for_username(self): return WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, #username)) ) def _wait_for_password(self): return WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, #password)) ) def _wait_for_login_button(self): return WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, #login-btn)) )登录这个动作暴露给用例层的只是一个login()方法用例写起来非常干净。将来前端改样式、改ID只动LoginPage这一个类十几个、几十个用例全部不用碰。5.2 数据驱动和用例分层让用例变成一张表Page Object解决的是页面怎么操作的问题接下来还有用例怎么写的问题。我倾向于把用例分成三层用例层只描述业务场景比如用户使用正确的用户名密码可以登录成功业务层封装一次完整的业务动作比如下单、支付、审核数据层管理输入数据比如测试账号、测试订单。用例层配合pytest的参数化可以直接用数据驱动的方式批量铺开import pytest class TestLogin: pytest.mark.parametrize( username,password,expected_text, [ (valid_user, correct_pwd, 登录成功), (valid_user, wrong_pwd, 用户名或密码错误), (, correct_pwd, 请输入用户名), ] ) def test_login_scenarios(self, driver, username, password, expected_text): login_page LoginPage(driver) login_page.login(username, password) toast Toast(driver) assert toast.get_text() expected_text这样的好处是新增一条测试用例的成本低到可以忽略而且用例本身的意图极其清晰任何人看参数列表就知道覆盖了哪些场景。5.3 测试数据管理不让用例互相坑害测试数据是Web自动化一个非常容易被忽略的深坑。如果两个用例同时注册同一个手机号或者一个用例删除了另一个用例依赖的数据跑出的结果就是随机失败。我在项目里会遵循三条黄金法则相互独立每条用例使用自己的数据不依赖别的用例所创建的数据可清理用例结束后通过界面返还数据、或通过接口清理、或使用数据库事务回滚确保环境回到基准状态数据工厂写一个简单的工具函数负责生成唯一的测试数据比如用户名后面拼上时间戳。def make_unique_user(): return { username: fuser_{int(time.time())}_{random.randint(1000, 9999)}, password: Test12345, email: fuser_{int(time.time())}example.com, }数据管理到位了偶发失败的出现概率会直线下降。你没法想象一个自动化平台上平均每天有几十条用例因为数据冲突而红脸排查起来有多痛苦。6. 踩坑实录那些让我熬夜排查的经典问题6.1 StaleElementReferenceException的根因与破解Selenium脚本最常见的异常之一就是StaleElementReferenceException。很多新手看到这条异常以为是自己写错了元素其实它的本质是你手里的元素引用已经过期了。在Selenium里每次find_element返回的是一个引用当页面局部刷新、页面跳转、DOM节点被前端框架替换之后这个引用指向的DOM节点已经不存在了你再对它操作就会抛异常。最常见的触发场景是你找到一个元素执行了一个会触发页面局部刷新的动作比如点击一个按钮表格刷新了然后你想对勾选的那一行继续操作但整个DOM已经被替换。解决办法其实也很简单就是在每一次DOM可能变化之后重新查找元素。比如row wait.until(EC.presence_of_element_located((By.XPATH, //tr[contains(., 测试订单)]))) row.find_element(By.CSS_SELECTOR, .delete-btn).click() # 点击后需要重新获取这行因为DOM已经刷新 wait.until(EC.staleness_of(row)) new_row wait.until(EC.presence_of_element_located((By.XPATH, //tr[contains(., 测试订单)])))这里的staleness_of是等待旧元素失效然后再重新获取新元素保证你手里握着的永远是活的引用。6.2 click()没反应或偶发失效click()没有报错但页面没有任何反应这是最让人抓狂的问题之一。因为你根本不知道是该去修代码还是重新跑一遍碰碰运气。我总结了一下偶发click失效的原因大概有这么几类元素被遮挡有个透明的遮罩层、loading弹层盖住了按钮Selenium认为元素存在并且可点击但实际点击落在了遮罩层上元素不在视口内页面太长按钮在页面底部浏览器没滚动到那个位置就强行点击某些场景下会注册不到事件元素处于disabled到enabled的转换瞬间你点击的瞬间按钮刚从灰色变可用前端事件监听还没绑定完成。解决思路一般是先等待element_to_be_clickable然后用JavaScript把元素滚到视野中心再点击submit_btn wait.until(EC.element_to_be_clickable((By.ID, submit))) driver.execute_script(arguments[0].scrollIntoView({block: center});, submit_btn) try: submit_btn.click() except Exception: driver.execute_script(arguments[0].click();, submit_btn)最后那个兜底是用JavaScript直接触发click事件能绕过一些前端事件覆盖的问题。但注意这是一个兜底方案不能成为默认格式否则会掩盖页面的真实可用性问题。6.3 多窗口和多标签页切换时的手忙脚乱处理弹出窗口、新标签页的时候最容易出现的错误是切到新窗口之后找不到元素切回主窗口之后又开始报错。关键在于弄清楚当前处于哪个窗口上下文。Selenium 4提供了更直观的API# 在原有窗口上打开新标签页 driver.switch_to.new_window(tab) # 或打开新窗口 driver.switch_to.new_window(window) # 获取所有窗口句柄 handles driver.window_handles # 按顺序切换 driver.switch_to.window(handles[-1])在新旧窗口之间反复切换时我的习惯是封装一个上下文管理器确保用完新窗口能自动切回主窗口避免用例后面莫名其妙在错误窗口里操作from contextlib import contextmanager contextmanager def new_tab(driver, url): original_handle driver.current_window_handle driver.switch_to.new_window(tab) if url: driver.get(url) try: yield finally: driver.close() driver.switch_to.window(original_handle)这种方式不仅代码干净更关键的是能把窗口状态的维护职责收拢在一处不再靠用例里逐个记着切换逻辑。6.4 页面加载策略网络慢时的超时误报有些用例在本地秒开到CI环境就时不时超时。其实不是脚本问题而是driver.get()默认要等整个页面加载完成才返回。如果页面里有个第三方统计脚本响应特别慢整个页面加载被拖到几十秒driver.get()自己就超时了。Selenium 4里可以用pageLoadStrategy来控制这个等待程度options webdriver.ChromeOptions() options.page_load_strategy eager # 等待DOM加载完成即可不等待图片和脚本 driver webdriver.Chrome(optionsoptions)eager模式在等待DOM ready之后就会继续执行脚本图片、广告、统计脚本这些不影响测试主流程的资源不再阻塞。对大部分测试场景来说用eager比用默认的normal要稳得多。我在某些素材比较重的页面测试时特意用过这个配置用例耗时从25秒降到8秒而且没有出现元素找不到的问题。7. 把自动化用例接入持续集成后才发现跑得稳比跑得多重要7.1 用例稳定率的指标化管理很多人刚把用例接入CI习惯用通过率作为唯一指标。但通过率会骗人。假设一百条用例里有一条偶发失败今天的通过率是99%明天是98%看起来还不错但时间都被浪费在反复确认是不是环境问题上了。我更建议把指标拆细稳定率同一用例连续跑N次的成功率低于95%的要重点优化首次通过率CI上每次跑完有多少用例一次通过失败重跑率依赖重试机制才通过的用例占比有多少平均执行时长用例数量增长时执行时间是否线性可控。某团队项目里引入了一个规则任何一条用例在本地连续跑十次有超过一次失败就不允许进CI主流程必须先把根因找到并修复。一开始大家觉得这个规则太苛刻但坚持了半年后自动化的可信度直线上升开发看到失败结果也能放心去排查。7.2 失败信息的自动沉淀截图、日志与干净的报告接CI之后最不缺的就是失败记录最缺的是一眼能看懂的失败记录。我接CI前做的第一件事就是把失败信息自动沉淀做成标配失败瞬间自动截图保存当前页面源码HTML记录浏览器控制台日志console log这一步对定位前端JS报错特别有帮助抓取当前URL以及发生异常时正在执行的操作步骤名称。有了这些信息定位问题基本不需要再跑到CI机器上看现场。报告方面我见过不少团队用Allure做结果聚合它能把参数化用例、失败截图、日志按层级整理得很清楚。不过选什么报告工具其实不是重点重点是报告必须能回答三个问题挂了哪条、为什么挂、是不是环境问题。7.3 从每天100条到每天1000条并行与分级的进化路径当用例量涨到千级串行执行的时间会彻底失控。这时候必须考虑并行执行和策略分级。并行方面如果是单机多线程可以给每条用例分配独立的浏览器会话如果想要更大规模再加上Grid或者容器化方案。但我想特别提醒一点并行不是免费的。并行度高到一定程度对被测系统的压力也上来了如果被测系统扛不住并发请求测试自己反而会制造出新的失败这时候你很难区分到底是测试环境问题、业务问题还是脚本问题。所以并行之前先做用例分级冒烟层最重要的主流程每次代码发布后立刻跑控制在十几分钟内回归层核心业务的大覆盖每天定时跑失败允许重试一次深度层冷门功能、跨浏览器矩阵、定时全量运行频率可以降到每周。我见过有人把所有用例一股脑全部塞进每次发布流程里结果发布一次要等四十分钟期间还夹杂各种偶发失败最后没人愿意看结果自动化形同虚设。合理的分级让CI在最快时间给出这次改动是否破坏了核心流程的信号才是自动化测试真正的价值所在。写在最后如果你问我这几年做Selenium Web自动化测试最想分享给后来人的一条经验是什么我想说先把一个场景做到稳定再铺开做大面积覆盖。我见过太多团队一开始就堆了一两千条用例结果每条都是在跑和不跑之间反复横跳最后连维护的人都没有了。我自己每写完一条用例都会在本机连续跑上5遍确认稳定了才提交。这个过程看起来很慢但恰恰是这套键盘敲回车之前先想想为什么可能失败的习惯让我后续花在定位问题上的时间越来越少。自动化测试是一件需要长期耐心的事工具只是起点稳定才是终点。希望这篇文章里的方法能帮你少走一些我走过的弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑