ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从Selenium到零代码录制回放:UI自动化测试维护成本直降50%的实践

从Selenium到零代码录制回放:UI自动化测试维护成本直降50%的实践

你是不是也经历过这种场景:项目上线前功能一堆堆地改,到了回归测试那几天,Selenium脚本报错红一整片。点开日志一看,不是按钮的xpath变了,就是弹窗多了一个遮罩层,明明是几分钟就能解决的问题,却因为脚本和页面耦合得太死,硬生生拖到半夜。后来我把脚本体系切到了一个零代码录制回放工具上,把元素定位和用例逻辑彻底拆开,维护成本肉眼可见地掉了接近一半。这篇文章就把我落地的思路、步骤、踩过的坑,以及为什么它能让维护成本降下来,一次说清楚。

1. Selenium脚本维护的痛到底痛在哪

1.1 维护成本的大头不是写脚本,而是改选择器

很多人以为做自动化测试,难点是把业务流程用代码写出来,其实不是。Selenium脚本写起来并不难,难的是它在页面面前太脆弱了。今天前端同学把按钮的class从btn-primary改成btn-confirm,你的脚本就挂了;明天某个接口超时,页面多渲染一个loading层,click没点到目标元素,又挂了。你回头看那些失败的用例,真正是业务流程判断错的很少,绝大多数都是元素定位失效、等待时间不够、页面结构微调这三类问题。

这类问题有个很讨厌的特点:单个来看都不复杂,但是量大、频发。一个中大型Web系统,页面元素几百上千个,每轮迭代只要有十分之一的页面动过,脚本就要跟着改一轮。改一个选择器本身只要一两分钟,可定位、排查、验证、回归这一套流程走下来,一个用例至少十几分钟。如果你维护着一套几百条用例的脚本库,每轮迭代花在“修脚本”上的时间,往往比真正跑回归的时间还多,加班修脚本就是这么来的。

1.2 脚本和页面过度耦合,是成本高的根源

传统Selenium写法里,元素定位信息和操作逻辑是揉在一起的。你在脚本里写find_element(By.XPATH, "/html/body/div[1]/div[3]/form/div[2]/input"),这个xpath不仅包含了页面的结构信息,还隐含着你对页面布局的假设。前端只要稍微调整一下DOM层级,这一段代码就废了。这就是过度耦合:你把“页面长什么样”和“我要对它做什么”写在了同一行代码里,页面一变,代码就得跟着变。

另外还有一层耦合更隐蔽,就是数据。你的脚本里如果硬编码了测试账号、订单号、商品名,那每次测试数据一变,脚本也要改。这部分的维护成本和元素定位还不太一样,它更麻烦,因为你得先搞清楚是数据的问题还是脚本的问题,排查链路更长。很多团队的自动化脚本,最后都是死在“不知道怎么改、不敢改、改了怕影响别的地方”这三点上。

1.3 传统解法只能缓解,不能根治

针对脚本脆弱的问题,常规思路是加显式等待、用稳定的CSS选择器替代xpath、封装公共方法。这些技巧有用,但只是缓解。你今天把xpath写得更健壮,明天前端改成动态渲染,元素加载顺序变了,照样挂。你今天封装了一个click方法,内部做了重试,可如果元素的name属性彻底变了,重试一百次也没用。

真正的问题是:你的自动化体系把“页面细节”和“业务流程”绑死在一起了。只要这个架构不变,维护成本就是无法消除的。我当时的结论是,与其继续在Selenium外面包一层又一层的“补丁”,不如换一个核心思路:把元素定位从脚本里剥离出来,交给一个独立的、可维护的、能让非开发人员也看得懂的层去管理。这也是我后来选择零代码录制回放工具的出发点。

2. 零代码工具的核心解题思路

2.1 对象仓库:把“页面长什么样”和“我要做什么”拆开

零代码录制回放工具的核心设计,是引入了对象仓库(也有人叫元素仓库)的概念。你在录制用例的时候,工具会自动把你操作过的每一个元素抓下来,生成一条元素映射记录,给这个元素命名,比如“登录按钮”“搜索框”“提交订单按钮”。用例里保存的只是一个元素名称,而不是具体的xpath或者CSS选择器。

等到用例运行的时候,框架再到对象仓库里去查这个元素名称对应的定位器。这样的话,你的测试用例本身是稳定的,变的只有对象仓库里的映射关系。前端改版了,你只需要重新抓取一次元素、更新这条映射记录,所有引用“登录按钮”的用例就全部自动修复了。举一个具体的例子感受一下:原来一个用例失败了,你要打开脚本、找到报错行、分析原因、改代码、提交并重新部署;现在只是打开元素管理界面,把“登录按钮”对应的新定位器替换一下,保存,重跑。

这就是维护成本下降的第一个来源:你从“改代码”变成了“改配置”。改代码需要对整个脚本逻辑保持高度的上下文记忆,改配置只需要知道这个元素现在叫什么、新定位是什么。前者是开发思维,后者是运维思维。对团队来说,后者显然门槛更低、效率更高。

2.2 录制回放:让用例的创建速度上一个台阶

零代码工具的第二个关键能力,是录制回放。Selenium脚本的传统写法是手写,你得先打开页面分析元素,再一步步把操作转换成代码。而录制回放工具的逻辑是,你在浏览器里像用户一样操作业务,工具在后台监听DOM变化和事件,把操作自动转换成用例步骤。

举个例子,你想做一个“登录-加购-下单”的流程用例。传统方式,你得先定位用户名输入框、密码输入框、登录按钮、商品链接、加入购物车按钮、结算按钮……这一套写下来,快一点的也要一个小时。用录制工具,你打开测试环境,手动输入账号密码,点击登录,搜索某个商品,加入购物车,去结算。操作完一遍,用例的骨架就自动生成了,你只需要检查一下步骤顺序、给关键步骤加上断言,就可以跑。整个过程,熟手大概十分钟。

我一开始对录制回放是有偏见的,总觉得它生成的代码会很乱、很难维护。后来实际用了发现,真正麻烦的并不是“生成用例”这一步,而是“页面上有这个元素,脚本里怎么指到它”这一步。录制工具恰好把这一层做成了对象仓库,生成的用例虽然不如手写那么精致,但胜在维护路径清晰。你日常需要动的,基本就是对象仓库的映射关系,不是用例本身。

2.3 自愈定位:失败时自动寻找备用定位器

再往下走,是自愈机制。这个功能听起来神乎其神,本质其实不复杂。工具在录制元素的时候,不只会记录一个定位器,它会把这个元素可能的所有定位方式都抓下来,比如id、name、class、xpath、以及它在哪个表单里、在哪个div范围内。运行的时候,如果首选定位器没找到元素,框架会自动按优先级去尝试备用定位器,全部尝试过还是找不到,才报告失败。

这个机制的意义,是把“一次定位失败”变成了“一次定位的尝试过程”。以前xpath失效就是彻底失败,现在id失效了会去试name,name失效了会去试相对位置。前端同事只要是正常重构,而不是把整个页面推倒重做,大概率有一个备用定位器能顶上。页面改版后的第一次运行,很多用例能靠自愈机制自动通过,维护的人只需要偶尔去更新一下对象仓库里的首选定位器。

当然自愈不是万能的,这个我后面在问题排查部分会详细讲。它解决的是“定位方式变化”的问题,解决不了“元素被彻底移除”的问题。理解这一点,你就不会对它抱有不切实际的期待。

2.4 为什么维护成本能降50%,这笔账怎么算

标题里写的直降50%,不是我拍脑袋编的数字,是我自己团队在两个季度里对比出来的。之前用纯Selenium维护的时候,每轮迭代大概要花两天时间处理脚本报错,其中一天半是在定位问题和改选择器,半天是处理数据问题和环境问题。切到零代码录制回放工具之后,同样规模的迭代,修脚本的时间压缩到一天以内,而且改的大部分是对象仓库映射,不再是改代码。

做个简单的账:假设一个项目每个月发两个版本,每次改版后修脚本需要两人各忙两天,相当于4个人天。切到新工具之后,变成一个人忙大概大半天,最多1个人天。按这个算,每月的维护工时从8个人天降到了2个人天左右。这还不算用例新建效率的提升,因为录制回放能把用例编写时间压缩到原来的三分之一甚至更低。两个因素加在一起,说维护成本直降50%是保守的。

我后来还观察到一个隐性的收益:团队里原本不敢碰自动化脚本的产品同事和测试新人,也开始能参与用例维护了。因为他们不需要理解代码,只需要知道“这个元素叫什么、现在对应哪个位置”。自动化测试这个事,从少数几个开发者的专属技能,变成整个团队都能上手的基础能力,这个价值其实比省下来那几个工时更重要。

3. 实操落地:从零接入零代码录制回放工具

3.1 工具选型要看这五个维度

市面上标榜“零代码自动化测试”的工具不少,但质量参差不齐。我在选型的时候主要看五个维度:定位器的丰富程度、对象仓库是否独立可维护、自愈机制的实际效果、用例的组织能力(是否支持分组、测试套件)、以及报告系统是否清楚。次要一些的还有执行器是否支持无头模式、能否集成到现有CI流程、有没有开放的API可以二开。

这里要特别提醒一点:不要只看演示视频里录制的顺滑程度,一定要自己动手,拿你们的真实业务页面去试用。很多工具在简单页面上表现很好,一遇到动态表格、复杂弹窗、多级菜单就原形毕露。你录完一个用例,回去重放一遍,看看脚本是不是每次都能稳定运行,尤其是隔一天再跑一次,很多坑才能暴露出来。

我当时选型花了大概一周,先后试了三款:有一款录制体验做得很好,但对象仓库管理太弱,元素多了以后乱成一团;还有一款报告很漂亮,但自愈机制基本是摆设。最后留下的是一个开源或者半开源的框架,社区活跃、API清晰、对象仓库的关系模型做得扎实。具体是哪款不重要,重要的是我在选型过程中总结出的这套标准,按这个标准去筛,大概率不会走眼。

3.2 第一步:搭建运行环境并跑通用例

我用具体步骤说说怎么落地。先说运行环境,你需要准备一台机器作为执行机,装好Chrome,下载对应版本的驱动(如果你用的工具是基于Selenium的,一般它会自动管理驱动版本)。如果你打算接入CI,建议把执行机做成容器或者用一个独立的agent,这样执行环境干净、可复现,不会因为机器上残留的进程干扰测试结果。

安装好工具本体之后,先做一个“冒烟验证”:打开工具自带或官方示例的项目,跑一遍内置用例,确认驱动启动、浏览器唤起、截图保存、报告生成这些基础流程是正常的。这一步看起来很基础,但能暴露一堆环境问题,比如驱动版本不匹配、浏览器沙箱权限不够、容器里缺少显示依赖等等。先用最简路径跑通,后面接入真实项目才不会一头雾水。

我第一次接入的时候,光环境就折腾了两个小时,最后发现是执行机上的Chrome自动更新了,驱动版本对不上。后来我把Chrome的自动更新关掉,并且在启动脚本里加了一段自动检测驱动版本和浏览器版本是否匹配的逻辑,这个坑就再也没出现过。

3.3 第二步:梳理核心流程并建立对象仓库

环境跑通之后,先别急着录用例。请你花半天时间,拉上熟悉业务的产品或前端同事,把系统里最核心的几条流程梳理出来。比如“登录-进入工作台-创建订单-确认提交”,或者“搜索商品-查看详情-加入购物车-结算”。为什么要先做这件事?因为零代码工具虽然降低了编码门槛,但不代表你不需要做测试设计。用例覆盖哪些核心流程、优先级怎么排、哪些场景需要断言,这些思考的过程,工具替代不了。

流程梳理完,就开始录元素。你要打开被测系统的页面,用工具的“元素拾取器”把关键元素一个个抓进对象仓库。这一步的操作要点是:给元素命名要规范,不要用“按钮1”“输入框2”这种名字,而要让它具备业务含义,比如“登录页_用户名输入框”“购物车_去结算按钮”。命名规范的好处是,后续维护和排查问题的时候,看名字就能大概判断是哪一层的元素。

给元素命名这事,团队最好统一一个约定。我常用的格式是:页面_模块_动作,比如“商品列表页_筛选_价格区间输入框”。对象仓库里元素多了以后,一个好名字比任何文档都管用。这一步千万别省,后面你就知道它的价值了。

3.4 第三步:录制核心用例并添加断言

元素仓库准备得差不多了,就可以录用例了。拿“登录-创建订单”这条主流程举例。启动录制,你手动在浏览器里输入账号密码、点击登录、进入订单页面、填写订单信息、点提交按钮。操作的同时,工具会把每一次点击和输入记成一个步骤,比如“点击 登录页_登录按钮”“输入 订单页_订单名称输入框 = 测试订单A”。

录完之后,用例列表里会出现一串步骤。你要做的事情有三件。

第一,检查步骤顺序,把多余的步骤删掉。比如录制过程中你可能不小心点开了一个下拉菜单又关掉,这种无效操作要清理掉。

第二,给关键步骤加断言。点击“提交订单”之后,加一个“断言 页面_提交成功提示 可见”,确保业务真正走到了成功状态。不加断言的话,就算全程步骤执行成功,也不能证明业务流程是对的,这是很多录制用例的致命伤。

第三,把用例里的硬编码数据改成变量。比如“输入 订单名称输入框 = 测试订单A”这一条,应该改成引用一个外部参数,这样同一个用例可以跑多组数据。这一步对应到Selenium的思路,就是把测试数据从脚本里抽出来,放到数据文件或者参数配置里。

录制完成不等于用例能用。你重放一遍,看看有没有步骤没录上、断言是不是判得太宽,反复跑两三次稳定了,再进回归套件。

3.5 第四步:接入套件管理与CI回归

当你有了一条稳定跑通的用例,接下来就是把用例组织成套件,纳入日常回归。零代码工具通常会提供用例分组的组织方式,你可以按照业务模块建分组,比如“登录注册”“订单流程”“支付流程”,再把用例拖进对应分组。套件管理这一步,决定了你的自动化测试体系是可规模化的,还是只停留在几个demo用例的层面。

接入CI的时候,我建议先用一套最小的冒烟集跑通流程。在CI配置里加一个Job,拉代码、安装依赖、启动工具、执行指定的套件、收集结果。跑通之后,再逐步把回归集加大。如果你所在团队的DevOps基础比较成熟,还可以把报告的链接回传给消息通知群,测试失败的时候自动艾特对应负责的同学。

这一步给我最大的感触是,零代码工具降低了写用例的门槛,但接入CI的过程和传统Selenium没有本质区别。CI流程里那些执行机管理、结果归档、失败通知的问题,并不会因为你用了零代码就自动消失。工具只是提升了用例产出和维护的效率,工程化的部分,还是得一步一个脚印做扎实。

4. 常见问题与排查技巧实录

4.1 高频问题速查表

我在实际使用中,整理了一张常见问题速查表,贴在这里供参考。

现象常见原因排查与解决办法
用例回放时元素找不到页面加载慢,录制时元素已出现,运行时还没渲染出来给该步骤增加显式等待,等待元素可见后再执行操作
同一元素部分用例失败页面在不同场景下渲染出不同的DOM结构在对象仓库里补充备用定位器,启用自愈定位
录制时操作正常,回放时位置偏移页面存在动态布局,点击的坐标被前端改动改用元素级定位,不要用纯坐标录制;勾选“相对定位”选项
断言结果不稳定断言条件太严苛(比如判断完整通知文案)或太宽松(只判断弹窗出现)拆成多条断言,用“元素可见+关键文本包含”组合判断
CI里跑不过,本地能过执行机缺依赖、显示器环境不同、网络策略导致静态资源加载慢用容器的无头模式,限制浏览器版本,把关键资源放到内网CDN
用例执行时间越来越长用例之间相互影响,没有做环境复位在套件开始前执行环境初始化,结束后清理数据还原快照

这张表不用背,你只要记住一个原则:录制的用例本质上也是自动化脚本,一样会受到环境、时序、数据隔离的影响。遇到问题,先按“环境问题、定位问题、数据问题、时序问题”四个方向去排查,比漫无目的地试要快很多。

4.2 录制的用例跑不起来,先别急着怀疑工具

很多新手第一次用录制回放工具,录完一条用例,回放失败,第一反应是“这个工具不行”。我的经验是,90%的失败不是工具的问题,而是你自己录用例的过程有问题。

最常见的录制失误有三个。一是录制前没有清理浏览器状态,上个用例登录过,你一打开页面就是登录态,录出来的用例根本没有“登录”这个步骤,下次执行环境一干净就挂了。二是录制过程中操作太快,工具来不及捕获某个元素的完整属性,生成的定位器不够稳。三是录制时网络好,回放时网络波动,页面加载慢,但你没有设置等待策略。

针对这三点,我的建议是:录制前先清缓存、退出登录、保证一个干净的初始状态;录制的时候放慢操作节奏,每一步操作之间停顿一两秒,给工具足够的时间捕获;录完之后立刻回放一遍,不要等到第二天再跑。如果回放失败,优先检查是不是等待时间的问题,再检查是不是元素没有录进对象仓库。把这些基本功打牢,你再去看工具本身,会发现它其实挺稳的。

4.3 动态元素和复杂组件处理技巧

复杂组件是零代码工具最容易翻车的地方,尤其是动态表格、下拉树、自定义弹窗这类。动态表格的问题在于,表格的行和列是变化的,你这次录制时定位的是第一行第一列,下次运行的时候,这一行已经不再是同一个数据。下拉树则更麻烦,它的选项层级深,展开方式特殊,普通点击操作很难稳定复现。

我的处理思路是,能不通过界面操作的,尽量不走界面。比如动态表格里的数据,如果你能通过接口先准备好数据,用例里就不需要去操作那一行数据。不得不走界面的时候,尽量去定位那个元素的不变属性,比如表格里的“文本内容”而不是“第几行第几列”,因为文本内容是业务数据决定的,行号是渲染位置决定的,前者稳定得多。

对于实在复杂的组件,还有一个土办法但很管用:写一个简单的扩展脚本挂到工具上,让专门的代码去处理复杂定位逻辑,工具主体继续用零代码的方式管理用例。相当于混合模式,简单场景用录制,复杂场景用自定义扩展。很多号称纯零代码的团队,最后都会走向这种混合模式,这不丢人,反而是务实。

4.4 数据驱动怎么在零代码里落地

零代码工具做得好的,基本都会提供数据驱动能力,只是名词叫法不同,有的叫“数据集”,有的叫“参数化”。做法就是把用例里输入的测试数据抽取成变量,然后在数据集里维护一行行数据记录,用例执行时按行读取、循环执行。

举个例子,你的登录用例里,用户名密码都设成了变量。数据集里放三行数据:第一行是正常账号,期望登录成功;第二行是错误密码,期望登录失败并出现错误提示;第三行是空密码,期望登录失败。执行一次套件,框架会自动把这组用例跑三遍,每一遍用不同的数据、验证不同的预期结果。

这里有一个细节容易被忽略:数据集里的预期结果字段,也要能映射到用例的断言里。很多工具默认只支持数据输入,不支持数据断言。如果遇到这种情况,我建议在断言步骤里增加一个变量引用,让它从数据集里读取“期望提示文案”,然后和页面实际文案进行比较。这样才能真正做到数据驱动,不然数据只进不出,断言还是写死的,效率会打折扣。

5. 长期维护的隐形收益与个人体会

5.1 涉及角色变多,自动化不再是少数人的事

这个感受是我落地两个月之后才意识到的。以前Selenium脚本放在代码仓库里,能改的人只有团队里那一两个会写代码的。产品经理想加一条用例,测试人员想调一个断言,都要先去找“负责脚本的人”。这个人一旦忙起来或者请个假,整个自动化回归就卡住了。这种单点依赖,是比脚本本身更贵的成本。

换成零代码录制回放工具之后,用例库变成了一个“看得见、摸得着”的东西。非开发角色打开工具,就能看到对象仓库里有哪些元素,用例库里有哪些流程,执行报告展示哪些用例失败。他们不需要理解xpath和CSS选择器,只需要在界面里操作。我的团队成员里,有一位对代码完全不熟的同学,我带着她录完第一组用例之后,她已经可以独立维护这个模块的用例了。这件事给我的触动很大:自动化测试的护城河,不应该建立在少数人的代码能力上。

5.2 维护重心从“修脚本”转移到“维护对象结构”

工具用久了,你会发现日常工作的重心发生了转移。以前是在日志和代码之间反复横跳,找是哪一行代码报错;现在是在对象仓库里,检查哪些元素映射过期了、哪个新页面的元素还没有录入。这个转移的本质,是把“处理紧急故障”变成了“做资产维护”。紧急故障是救火,资产维护是常态化的保养。前者让人疲惫,后者让人安心。

我建议你从落地第一天起,就把对象仓库当资产来经营。定期检查有没有长时间未使用的元素,及时清理;每次前端上线前先同步一份改动清单,提前更新对象仓库的映射;元素命名建立规范,新团队成员进来第一课就是讲命名约定。这些事情看着繁琐,但积累一段时间之后,你的自动化体系会越跑越顺。

5.3 一些实用小技巧

最后分享几个我在实际操作中总结的小技巧,不算什么高深的东西,但确实帮我省了不少时间。

第一,录制用例时尽量把操作节奏放慢,特别是从列表页进入详情页这种跨页面的操作,停顿两三秒再操作下一步,录制出来的定位器会更完整。第二,对象仓库里同一元素的不同定位器,优先级不要随便调,首选定位器应该是最稳定、最不容易变化的那一个,比如id和name,再往后放class和xpath。第三,断言不要只断言一个点,登录成功你可以断言“首页可见+用户名文本正确+右上角头像出现”,三重断言更可靠。第四,如果遇到顽固的时序问题,与其在一个步骤上加长等待时间,不如在这个元素加载之前插入一个“点击某个空白区域”的步骤,强制页面触发一次重渲染,实测下来往往比干等更有效。

这些技巧看起来零散,但它们在真实项目中帮我躲过了不少麻烦。零代码录制回放工具并不能让自动化测试从“有成本”变成“零成本”,它能做的是把成本从“不可预测的修代码”变成“可管理的维护资产”。只要你花心思把对象仓库和用例结构梳理好,维护成本降50%不是一句空话。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进