简介这是一套面向Web自动化测试初学者与测试工程师的脚本录制工具包围绕Chrome浏览器中的Katalon Recorder展开旨在降低Selenium脚本编写门槛帮助读者快速完成操作录制、回放与基础编辑适用于功能测试、回归验证和测试入门练习。压缩包共4个文件包含2个crx浏览器扩展与2份html安装使用说明整体约3.95MB体积轻便便于部署查阅。已有1522人学习下载具备一定参考热度。包内资料可用于搭建录制环境、了解扩展安装方式并借助说明文档熟悉脚本生成、断言添加、循环组织和回放验证等环节附带的标签页增强插件也可优化日常浏览体验。对于希望以较低学习成本理解自动化测试流程、积累Web测试实操思路的读者这份工具包提供了较为直观的参考入口。1. 录制只是起点一次 40 分钟的回归Katalon Recorder 能省下哪三十分钟周一早上例行回归要跑。登录、建单、改状态、导出报表一套流程点了四十分钟点完还得对着截图确认哪一步没报错。这种重复劳动正是脚本录制工具存在的理由。Katalon Recorder 是个浏览器扩展形态的自动化录制器点开就开始录把人在页面上的点击、输入、下拉、跳转翻译成一行行可回放的命令录完能直接重新跑也能导出成 Python、Java、Robot Framework 等代码交给工程化流水线。它适合三类人刚接手 Web 回归、还没搭起框架的测试同学想快速摸清某个系统页面交互路径的开发以及手上有一堆手工用例、想让其中三成先跑起来的团队。但它不是“录完就完事”的东西录制只是拿到了初稿能不能长期用取决于后面怎么改。这一篇按我个人拆包使用的顺序讲命令结构、定位器改写、套件组织、等待与断言、踩坑排查最后落到怎么把它塞进命令行回归。2. 命令三要素与定位器把“录下来能跑”改成“放三天还能跑”录制工具最容易让人产生错觉的一点是它在录制的那一刻确实跑通了。可第二天页面加了埋点、按钮外包了一层 div、列表改成虚拟滚动昨天那套脚本就集体趴窝。问题几乎都出在两处——命令本身写得不够精确定位器选得不够稳。先把这两块的地基打牢后面导代码、上流水线才有意义。2.1 一行脚本的三个格子command / target / value打开 Katalon Recorder 的编辑器主体是一张表每行三个格子。command 是要执行的动作target 是动作作用在谁身上value 是动作带的参数。理解这三格的语义分工比背命令名重要得多。类别典型命令target 放什么value 放什么页面动作open、click、type、select元素定位器或 URL输入文本、选项值取值存储storeText、storeAttribute、store元素定位器变量名断言校验assertText、verifyElementPresent元素定位器期望文本等待同步waitForElementPresent、waitForVisible元素定位器超时毫秒数流程控制if、elseIf、while、echo条件表达式提示文本补时机pause空毫秒数录制生成的行绝大多数落在前两类。这里有个细节值得注意type和sendKeys不是一回事。type会先清空输入框再写sendKeys是追加敲键遇到“搜索框里已有上次关键词、需要接着输”的场景用错命令就会出现“怎么变成了拼在一起的字符串”这种莫名其妙的失败。我一般默认用type只有明确要模拟逐字输入触发联想搜索时才换sendKeys。storeText是录制器里被严重低估的命令。它把页面上的文本抓进变量后面可以用${变量名}的形式在 value 里引用。比如下单流程要拿订单号去查详情录制器本身不会自动帮你记住这个号得手动插一行storeText把订单号存下来后面type到搜索框时写${orderNo}。这一步不补脚本就只能跑固定数据跑第二遍就撞号。2.2 定位器优先级id → name → css → xpath 的取舍录制器默认倾向于生成 xpath原因是它最通用——几乎所有元素都能被 xpath 描述出来。但“能描述”和“描述得稳”是两件事。一个自动生成的绝对路径可能长这样//div[3]/div[2]/form/div[1]/input这种路径把每一层兄弟节点的序号都写死了。前端只要在表单前面插一个提示条序号全变脚本立刻失效。这不是工具的问题是绝对路径这种描述方式在结构变动面前天然脆弱。我实际改写时按这个优先级走id——最稳除非它是框架自动生成的、每次刷新都变。判断方法很简单刷新三次页面看 id 后面那串数字变不变。name——表单元素常见稳定性接近 id但同名元素可能不止一个要确认唯一性。>{ id: a1b2c3d4-0000-0000-0000-000000000000, version: 2.0, name: 订单回归套件, url: https://example.internal/app, tests: [ { id: t-001, name: 新建订单并提交, commands: [ { id: c-0001, comment: 打开下单页固定入口不依赖菜单导航, command: open, target: /order/new, value: }, { id: c-0002, comment: 等表单容器出现代替写死 pause, command: waitForElementVisible, target: css[data-testidorder-form], value: 15000 }, { id: c-0003, command: type, target: css[data-testidinput-customer], value: 测试客户A }, { id: c-0004, command: click, target: css[data-testidbtn-submit], value: }, { id: c-0005, comment: 抓订单号存变量供后续用例引用, command: storeText, target: css[data-testidorder-no], value: orderNo } ] } ], suites: [ { id: s-001, name: 冒烟集, persistSession: false, parallel: false, timeout: 300, tests: [t-001] } ], urls: [https://example.internal/app], plugins: [] }几个字段值得单独说。tests[].commands是真正执行的内容一条命令一个对象comment字段是给人看的建议每三到五条命令补一句否则三个月后回来没人看得懂当时为什么在这插了个等待。suites[].timeout是单条用例的超时秒数不是单条命令的设太小会把加载慢的页面误杀我一般给 300。persistSession控制是否在用例间保留登录态涉及登录的套件打开它能省掉反复登录的几十秒。parallel在纯录制场景里通常用不上跨用例共享浏览器状态时打开反而会互相干扰。因为它是 JSON所以可以做很多脚本化的事用jq统计命令总数、批量把pause换成等待命令、对比两个版本的差异看谁改了什么。把它放进 Git改版导致的定位器变更就有了 diff 可查这比在浏览器里改完忘了改哪强太多。3.2 用例、套件、公共步骤怎么拆录制器最容易滑向的一个极端是把整条业务链路录成一条巨型用例二百多行命令首尾相连。这种结构没法定位失败点也没法复用。我的拆法是三层第一层是原子用例只做一件事比如“登录成功”“新建订单”“取消订单”。每条命令控制在三十行以内起名要能一眼看出干什么。第二层是公共步骤前置。登录、切换角色、清空草稿这些每条用例都要走一遍的动作单独录成一条用例在套件里排在前面后面的用例依赖它的执行结果。这里要注意persistSession必须打开否则每条用例都重新开一个干净的浏览器上下文登录态就丢了。第三层是场景套件把原子用例按业务链路串起来。冒烟集只放五到八条核心路径控制在五分钟内跑完回归集可以放长一些但别指望一个套件跑完全部跑两小时没人盯中间任何一个环境抖动都会连累后面的用例。拆的时候有个判断标准如果一条用例失败后你需要翻代码才知道它到底在哪一步挂的那就说明这条用例该拆了。失败的定位成本比用例数量更能说明结构好坏。3.3 导出代码Python 与 Java 各适合什么场景录制器能导出多种语言常用的两类是 Python 和 Java。导出的本质是把 JSON 里的命令翻译成对应语言的 WebDriver 调用所以它必然丢掉一些东西——最常见的是等待语义和变量作用域这两处往往是导出后跑不通的直接原因。下面是一段导出后的 Python 骨架我做过少量整理保留它原本的姿态# 由录制文件导出的 Python unittest 骨架已补注释 import unittest 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 class TestOrderCreate(unittest.TestCase): def setUp(self): # 录制器导出的部分通常不带这些配置需要手动补 options webdriver.ChromeOptions() options.add_argument(--window-size1440,900) # 无头模式跑 CI 时打开本地调试建议关掉方便看现场 # options.add_argument(--headlessnew) self.driver webdriver.Chrome(optionsoptions) self.driver.implicitly_wait(3) # 兜底隐式等待别设太大 def test_create_order(self): d self.driver d.get(https://example.internal/app/order/new) # 原本录制的是 pause 5000这里换成显式等待 WebDriverWait(d, 15).until( EC.visibility_of_element_located((By.CSS_SELECTOR, [data-testidorder-form])) ) d.find_element(By.CSS_SELECTOR, [data-testidinput-customer]).send_keys(测试客户A) d.find_element(By.CSS_SELECTOR, [data-testidbtn-submit]).click() # 等订单号出现再取值避免读到空节点 order_no WebDriverWait(d, 15).until( EC.visibility_of_element_located((By.CSS_SELECTOR, [data-testidorder-no])) ).text self.assertTrue(order_no.strip(), 订单号为空提交可能没生效) def tearDown(self): self.driver.quit() if __name__ __main__: unittest.main()这段代码里有两个参数值得调implicitly_wait(3)是全局兜底设得太大会让所有找不到元素的操作用乘以轮询次数的方式变慢我一般给 3 秒封顶主要靠显式等待干活WebDriverWait(d, 15)里的 15 是单次等待上限页面首屏慢的项目可以到 20接口快的后台系统 10 就够。至于选 Python 还是 Java项目里有现成的 pytest 或 unittest 脚手架就导 Python接进去快团队主栈是 Maven 或 Gradle导 Java 更省事别为了脚本另起一套构建体系。至于导出的代码只是起点这件事得反复强调——导出脚本里往往残留一堆time.sleep和绝对 xpath直接提交进仓库等于把录制阶段的债原封不动搬进代码库。3.4 数据驱动CSV 与传统 storeText 两条路录制器支持挂 CSV 做数据驱动这对“同一流程、多组数据”的用例是刚需。写法是准备一份表头清晰的文件在命令里用${列名}引用。username,password,expectTip user_ok_01,Pass123,登录成功 user_ok_02,Pass123,登录成功 user_bad_01,WrongPass,密码错误命令表里对应写成open | /login | type | css[data-testiduser] | ${username} type | css[data-testidpwd] | ${password} click | css[data-testidbtn-login] | waitForElementVisible | css[data-testidtip] | 10000 assertText | css[data-testidtip] | ${expectTip}要点有三CSV 必须存成 UTF-8 无 BOM带 BOM 时第一列表头会多出不可见字符表现是“第一列取值永远是空”这个坑几乎人人踩过一次空行要删掉录制器会把空行当一组空数据跑一遍然后报一堆莫名其妙的失败期望值也放进数据里而不是在断言里写死否则三组数据只能验证同一种结果。另一条路是前面提过的storeText配合${变量}适合“数据是运行时产生”的场景比如上一步生成的订单号、随机手机号。两条路可以混用CSV 提供输入storeText接住输出再传给下一步。4. 等待与断言flaky 用例的八成来自这两块脚本跑一次过、跑十次挂三次这种玄学现象在录制式自动化里太常见了。绝大多数不是元素定位错了而是脚本在页面还没准备好的时候就动了手或者断言写得太松问题被吞掉了。4.1 三种时序错误没出现、已消失、可点但点不动第一种是元素还没出现。录完立刻跑能过是因为录制时页面在缓存里加载飞快到 CI 的冷启动环境接口多绕两跳三秒变十秒脚本一进去就找不到元素。第二种是元素已经消失。典型场景是 loading 遮罩、骨架屏脚本抓到的其实是即将被替换掉的占位节点点上去的瞬间它被真实内容顶掉了报“元素不可交互”。第三种最隐蔽元素存在、可见但不可点击。按钮被透明遮罩盖着或者还在禁用态。这时候waitForElementPresent会立刻返回成功因为元素确实在 DOM 里真正该等的是“可点击”。对应三种等待就不一样等出现用waitForElementPresent或waitForElementVisible等消失用waitForElementNotVisible等可点击用waitForElementClickable部分版本叫法不同本质是等待元素 enabled。用错命令等于没等。4.2 等待命令的参数怎么设超时值不是越大越好每条等待命令的 value 是超时毫秒数。参数怎么给我的经验是按元素类型分档元素类型建议超时说明本地渲染的静态元素5000再慢就是代码有问题依赖接口的首屏容器15000冷启动环境留足缓冲复杂报表、导出结果30000后端计算耗时别硬卡异步校验提示10000太短会误判为未出现轮询型状态变化60000配合循环用别单条死等超时值设成 120000 看着“保险”实际是把失败检测时间拉长到两分钟。真正该做的是给一个合理上限同时在脚本里保留失败截图让超时发生时能立刻看到现场长什么样。翻车的往往不是等待太短而是等待太长把一次本该快速暴露的环境问题拖成了二十分钟的挂起。另一个技巧是尽量用等待命令替换pause。pause 5000是纯粹的盲等每次执行都实打实花五秒一条用例插三个就十五秒一百条用例就是一小时。换成条件等待之后页面一就绪立刻继续正常路径几乎零开销。4.3 assert 与 verify 的取舍什么时候该中止assert系列在失败时中止当前用例verify系列失败只记一条日志继续往下跑。这个差别决定了你能拿到多少信息。我的用法是分层关键路径用 assert比如登录后没有跳到首页、下单后订单号为空这种一旦失败后面全无意义早停早省时间非关键校验用 verify比如列表字段格式、按钮文案、分页数量这些失败不该阻断后续步骤攒一批一起看更有价值。有个反直觉的点断言太密反而降低脚本的可用性。见过一条用例里塞了四十个verifyText每次改版都要逐条核对文案维护成本远超它发现的问题。断言的密度应该和业务重要性挂钩不是越多越显得严谨。4.4 全局超时、失败截图与日志级别套件级timeout是兜底防线防的是某条命令卡死不返回。设成 300 秒是常见值长流程的可以到 600。超过这个值还没跑完基本可以判定是环境问题或者等待逻辑有死结。失败现场比失败本身重要。录制器自带的日志面板能看到每一步的执行状态和耗时排查时先看哪一步开始变慢——往往不是失败那一步本身有问题而是它前面某一步等了很久才勉强通过。如果导出成了代码务必在tearDown里补截图和页面源码落盘这两份东西能把“找不到元素”的扯皮时间从半小时压到两分钟。5. 避坑排查五个翻车现场的现象、原因与解决5.1 定位与框架类元素明明在页面上脚本就是找不到现象一手动能看到输入框脚本报找不到元素日志里页面结构也正常。原因基本是元素在 iframe 里。WebDriver 的查找作用域停留在主文档不切进去永远找不到。 解决是先切框架再操作selectFrame | cssiframe[data-testidpay-frame]操作完用selectFrame | relativetop切回主文档。嵌套 iframe 要层层切切错层级的表现是“元素找到了但点击无效”。容易忽略的是很多弹窗表单跑在 iframe 里录制时因为鼠标操作由浏览器原生处理所以能录上回放时才暴露。现象二点了按钮页面开了新标签页或弹出新窗口后面所有命令全挂。原因是脚本还停留在旧窗口句柄上。 解决办法是显式切窗口。录制器里有selectWindow系列命令按标题或句柄切换。我的习惯是在打开新窗口的点击之后立刻插一行waitForWindowPresent再selectWindow过去操作完切回原来的窗口。不切回来的后果是后续命令在错误的窗口里找元素报错信息看起来毫无关联。现象三同一条用例今天过明天挂定位器里那串数字每天都在变。这是动态 id。组件库或前端框架按运行时自增生成 id刷新就变。 解决是把定位器改成前缀匹配或换属性。用xpath//input[starts-with(id,field-)]或者直接找它稳定的父级再往下定位。更好的做法是推动前端加># 去掉 BOM 并转成 LF 换行避免跨平台跑出诡异差异 sed -i 1s/^\xEF\xBB\xBF// data/login.csv dos2unix data/login.csv第二个原因是文件末尾有多余空行数据驱动会把它当成一组空记录跑一遍。第三个原因是列名大小写或空格不一致username和userName在引用 ${} 时并不等价。这三处检查完数据驱动基本就稳了。还有一个不在上列但值得单独提醒的导出后的脚本没带上等待。录制器里的waitForElementVisible导出成代码时某些目标语言会退化成隐式等待或直接被丢掉表现是导出的脚本在本地跑得好好的进了 CI 就超时。导出后手工过一遍所有等待逻辑是省不掉的工序。6. 进阶把 .side 文件纳进版本管理用命令行跑回归录制器在浏览器里点着用很舒服但它真正的价值在于.side是一份可以被工程化处理的文本。把这份文件当源码对待整条链路就活了。常见做法是用命令行执行器直接跑.side文件比如基于 Node 的那套 side runner装好之后一条命令就能拉起浏览器执行整个套件# 安装命令行执行器全局一次即可 npm install -g selenium-side-runner # 跑单个套件--base-url 用来覆盖录制时写死的地址 selenium-side-runner order.smoke.side \ --base-urlhttps://staging.internal/app \ --output./reports \ --agents 2 # 只跑名字匹配的用例改版验证时很省时间 selenium-side-runner order.smoke.side -f name登录*--base-url是最值钱的参数。录制时地址被写死在命令里有了它同一份套件可以指向测试环境、预发环境、本地起的那套服务不用改文件。--agents 2控制并发数超过 2 要谨慎——录制式脚本之间如果共享同一套后台数据并发会互相踩出现只在并行时复现的失败。-f过滤用例名改版后只跑受影响的几条比全量跑快得多。配合 Git 的分支流程我会这么组织.side文件放仓库根目录的e2e/下一次改版一个分支改版后先本地跑冒烟集确认定位器没塌再合进去。因为文件是 JSONdiff 出来能清楚看到谁把哪条命令的 target 改了评审时一眼就知道改得对不对。上线前我习惯走一遍这张检查表五项都过再提交检查项通过标准不通过时的典型症状定位器稳定性无绝对路径 xpath无动态 id隔天跑就挂等待方式无裸 pause 超过 2000msCI 上大面积超时断言覆盖关键路径有 assert页面白屏也报成功数据隔离每次跑不依赖上次残留数据连跑两次第二次必挂套件时长冒烟集 5 分钟内跑一半就没人守着了最后说个我自己的教训。早些年我把录制脚本当一次性工具用录完跑通就丢下次改版重新录一遍觉得这样最省事。直到某次一个支付流程改版重新录的时候漏掉了一个“余额不足”的分支结果线上出了问题才想起来——那条分支当初是手动补的本地磁盘上根本没留档。从那以后我每次录完都强制走一遍「导出 .side → 提交仓库 → 补注释」的流程哪怕这条用例只用一次。录制工具帮我们省的是敲代码的时间不是思考的时间把录下来的东西当资产管起来它才会真的开始省钱。希望帮到你。本文还有配套的精品资源点击获取