SeleniumBase零代码录制与JSON自动化测试方案:从录制到执行的完整实践

发布时间:2026/7/6 9:46:33
SeleniumBase零代码录制与JSON自动化测试方案:从录制到执行的完整实践 1. 项目概述当“零代码”遇上“结构化数据”如果你和我一样在自动化测试领域摸爬滚打了好些年肯定经历过这样的场景业务方或者产品经理拿着一个刚上线的功能页面急切地问“这个流程能加个自动化测试吗越快越好” 而开发同学可能正忙于下一个迭代没时间写测试脚本。传统的Selenium脚本编写从元素定位到逻辑编排再到异常处理一套流程下来没个半天一天搞不定而且对测试人员的编码能力要求不低。这就是“SeleniumBase零代码录制与JSON自动化测试方案”要解决的核心痛点。它不是一个全新的框架而是一种基于成熟工具的、高效且低门槛的实践组合拳。简单来说它的核心思想是用录制工具生成操作步骤用JSON文件描述测试逻辑再用SeleniumBase作为执行引擎。这样一来不懂Python或者对Selenium API不熟的同学也能快速构建出稳定、可维护的UI自动化测试用例。为什么是JSON从热搜词里能看到“json格式”、“json解析”、“json文件”的热度一直很高。JSON作为一种轻量级的数据交换格式结构清晰、易于读写既是人可读的配置文件也是机器可解析的指令集。用它来承载测试步骤、断言和数据正好将“做什么”业务逻辑和“怎么做”代码实现解耦。测试设计者可以专注于业务流和测试数据而执行环境SeleniumBase负责忠实地还原这些指令。这个方案特别适合哪些场景呢首先是快速验证比如冒烟测试、核心业务流程的回归测试。其次是团队协作测试用例以JSON文件形式存在方便版本管理Git也便于不同角色产品、测试、开发评审测试逻辑。最后是测试数据驱动你可以轻松准备多组JSON数据实现同一套操作流程、不同测试数据的批量执行。2. 核心工具链选型与原理拆解工欲善其事必先利其器。要实现这个方案我们需要一套清晰的工具链。这里的核心是“录制”、“结构化”和“执行”三个环节。2.1 录制工具从“手工操作”到“可执行指令”录制是零代码的起点。我们需要的不是一个简单的“录屏”工具而是一个能记录浏览器交互事件点击、输入、选择等并生成结构化操作序列的工具。Selenium IDE是首选。它是一个浏览器插件支持Chrome和Firefox可以录制你在网页上的所有操作并生成多种格式的脚本包括它自己的.side格式。虽然它原生支持导出为Python Selenium代码但我们的目标不是代码而是更结构化的中间表示。不过Selenium IDE导出的.side文件本身就是JSON格式的这为我们提供了完美的原材料。一个.side文件大致结构如下{ id: test-case-1, name: 用户登录测试, tests: [{ id: step-1, name: 打开登录页, commands: [{ id: cmd-1, command: open, target: /login, value: }] }, { id: step-2, name: 输入用户名, commands: [{ id: cmd-2, command: type, target: idusername, value: test_user }] }] }可以看到它已经包含了命令command、目标target即元素定位器和值value这三个关键信息。这就是我们JSON测试用例的雏形。注意Selenium IDE的定位器策略可能比较单一如大量使用XPath在后续的“结构化”环节我们可能需要对其进行优化和转换以提升稳定性和可读性。2.2 结构化核心设计可读、可扩展的JSON Schema直接从Selenium IDE导出的.side文件可以用但不够友好和强大。我们需要设计一个更贴合业务、更易于维护的JSON结构。这是整个方案中最体现“设计”的部分。我们的目标JSON测试用例文件应该包含以下核心部分元信息用例ID、名称、描述、所属模块、优先级等。前置/后置条件执行前需要准备什么如打开特定URL、登录执行后需要清理什么如登出、删除测试数据。测试步骤序列核心部分。每个步骤应包括步骤描述、操作类型、元素定位器、操作值、等待策略、断言等。测试数据可以与步骤分离实现数据驱动。例如将登录用户名/密码作为外部数据源注入。一个优化后的JSON测试用例示例{ testcase: { id: TC_LOGIN_001, name: 验证用户使用正确凭据登录成功, description: 登录功能的核心正向流程测试, module: 用户认证, priority: P0, preconditions: [ { action: navigate, url: https://example.com/login, description: 打开登录页面 } ], steps: [ { step_id: 1, description: 在用户名输入框输入有效用户名, action: input_text, locator: { strategy: css selector, value: #username }, value: ${data.username}, wait: { type: visible, timeout: 10 } }, { step_id: 2, description: 在密码输入框输入有效密码, action: input_text, locator: { strategy: css selector, value: #password value: ${data.password}, secure: true // 标记为敏感信息日志中可脱敏处理 } }, { step_id: 3, description: 点击登录按钮, action: click, locator: { strategy: xpath, value: //button[text()登录] } }, { step_id: 4, description: 验证登录成功后跳转到首页, action: assert, assertion: { type: url_contains, expected: /dashboard }, wait: { type: url_changed, timeout: 5 } } ], postconditions: [ { action: click, locator: { strategy: link text, value: 退出 }, description: 安全退出清理会话 } ] }, data: { username: standard_user, password: secret_sauce } }设计要点解析locator对象明确指定定位策略css selector,id,xpath等比Selenium IDE的单一字符串更清晰也便于后续统一处理。wait对象将显式等待策略集成到步骤中这是编写稳定自动化测试的关键。可以支持visible元素可见、clickable元素可点击、presence元素存在等多种类型。assertion对象将断言作为一等公民。支持多种断言类型如text_equal文本等于、element_present元素存在、url_containsURL包含等。数据分离使用${data.username}这样的占位符将测试数据与操作流分离。同一份JSON用例搭配不同的数据文件就能运行多次。安全字段secure: true标记敏感信息在执行引擎中处理日志输出时可以进行脱敏避免密码泄露。2.3 执行引擎SeleniumBase的强大驱动为什么选择SeleniumBase而不是原生Selenium原生Selenium WebDriver只是一个浏览器控制接口要构建一个健壮的测试框架你需要自己处理很多事等待机制、报告生成、失败截图、并行执行、Fixture管理如setUp/tearDown等。SeleniumBase在Selenium之上做了极好的封装它内置智能等待自动处理大多数等待场景减少time.sleep的滥用。强大的命令行工具一行命令即可运行测试、生成报告。精美的测试报告内置基于pytest-html的报告包含截图、日志信息丰富。简化API提供了更简洁易记的方法名如self.click(selector)self.type(selector, text)。与pytest深度集成可以直接使用pytest的所有强大功能如参数化、Fixture、插件等。我们的方案中SeleniumBase扮演“翻译官”和“执行官”的角色。我们需要编写一个“JSON解析执行器”这个执行器是一个SeleniumBase测试类它的核心任务是读取我们设计好的JSON用例文件解析每一个步骤将其翻译成对应的SeleniumBase API调用并执行。3. 从录制到JSON完整工作流实现理解了核心组件我们来看如何将它们串联起来形成一个端到端的工作流。这个过程可以分为四个阶段录制、转换、增强、执行。3.1 第一阶段使用Selenium IDE录制原始操作安装插件在Chrome或Firefox商店中搜索“Selenium IDE”并安装。创建新项目打开Selenium IDE创建一个新项目为其命名例如“用户登录流程”。开始录制点击录制按钮输入目标网站的URL如测试环境的登录页地址。Selenium IDE会打开一个新浏览器窗口并开始记录你的所有操作。执行测试操作像真实用户一样完成整个测试流程。例如输入用户名、输入密码、点击登录、检查登录后的元素。停止录制并保存操作完成后回到Selenium IDE窗口停止录制。你会看到所有操作被记录为一条条命令。将其保存为.side文件。实操心得录制时操作尽量线性、简洁避免不必要的鼠标移动和点击。对于需要验证的点记得使用IDE的“Assert”或“Verify”命令这些断言也会被记录下来成为我们后续JSON中断言的基础。3.2 第二阶段开发转换脚本将.side转为定制JSON拿到.side文件后我们需要一个Python脚本将其转换成我们之前设计的、更友好的JSON格式。这个脚本主要做几件事解析.side文件.side是JSON直接用json.load()读取。映射命令将Selenium IDE的命令type,click,assertText等映射到我们自定义的action类型input_text,click,assert_text_equal。重构定位器分析IDE生成的target字段尝试将其标准化为我们的locator对象。例如将idusername解析为{“strategy”: “id”, “value”: “username”}。对于复杂的XPath可以保留原样。提取断言将IDE中的断言命令转换为步骤中的assertion对象。输出定制JSON按照我们的Schema生成新的JSON文件。转换脚本核心函数示例import json import os def convert_side_to_custom_json(side_file_path, output_dir): with open(side_file_path, r, encodingutf-8) as f: side_data json.load(f) # 假设.side文件中第一个测试套件的第一个测试用例是我们录制的 tests side_data.get(tests, []) if not tests: print(未找到测试用例) return first_test tests[0] custom_testcase { testcase: { id: first_test.get(id, ), name: first_test.get(name, 未命名用例), description: , module: Recorded, priority: P2, preconditions: [], steps: [], postconditions: [] }, data: {} } # 转换命令为步骤 commands first_test.get(commands, []) step_counter 1 for cmd in commands: command cmd.get(command, ) target cmd.get(target, ) value cmd.get(value, ) step {step_id: step_counter, description: f步骤{step_counter}} # 命令映射逻辑 if command in [open]: # 如果是打开URL可以放到preconditions里或者作为一个特殊步骤 custom_testcase[testcase][preconditions].append({ action: navigate, url: target, description: f打开页面: {target} }) continue # 不作为一个普通步骤计数 elif command in [type, sendKeys]: step[action] input_text step[locator] parse_locator(target) step[value] value elif command in [click, clickAt]: step[action] click step[locator] parse_locator(target) elif command.startswith(assert): step[action] assert step[assertion] parse_assertion(command, target, value) # 断言步骤通常不需要locator和value或者target就是locator if target and not target.startswith(http): step[locator] parse_locator(target) else: # 其他命令如pause可以转换为等待或直接忽略 print(f忽略未处理的命令: {command}) continue step[wait] {type: visible, timeout: 10} # 添加默认等待 custom_testcase[testcase][steps].append(step) step_counter 1 # 保存转换后的JSON output_file os.path.join(output_dir, f{custom_testcase[testcase][id]}.json) with open(output_file, w, encodingutf-8, indent2) as f: json.dump(custom_testcase, f, ensure_asciiFalse) print(f转换完成文件已保存至: {output_file}) def parse_locator(target_str): 将Selenium IDE的target字符串解析为标准locator对象 if target_str.startswith(id): return {strategy: id, value: target_str[3:]} elif target_str.startswith(css): return {strategy: css selector, value: target_str[4:]} elif target_str.startswith(xpath): return {strategy: xpath, value: target_str[6:]} elif target_str.startswith(link): return {strategy: link text, value: target_str[5:]} else: # 默认当作CSS选择器处理或者尝试智能解析 # 这里简单处理直接使用原字符串作为CSS选择器可能不稳定 return {strategy: css selector, value: target_str} def parse_assertion(command, target, value): 解析断言命令 assertion_map { assertText: {type: text_equal, expected: value}, assertTitle: {type: title_is, expected: value}, assertElementPresent: {type: element_present} } return assertion_map.get(command, {type: custom, command: command, target: target, value: value})这个转换脚本是自动化的关键一步它将“录制结果”变成了“可被引擎理解的标准化指令”。3.3 第三阶段手动优化与增强JSON用例自动转换出来的JSON是“毛坯房”我们还需要进行“精装修”。这个阶段需要人工介入也是提升测试用例质量和稳定性的关键。优化元素定位器检查并修正自动转换的定位器可能不稳定如绝对XPath。手动将其替换为更稳定、更简洁的定位方式优先使用id、name其次是css selector最后才是xpath。添加备用定位器可以在locator对象中增加一个fallback字段当主定位器失效时尝试备用定位器提高鲁棒性。locator: { strategy: css selector, value: #submitBtn, fallback: { strategy: xpath, value: //button[typesubmit] } }细化等待策略转换脚本添加的是通用等待。现在需要根据具体步骤调整。例如点击按钮前等待按钮变为可点击”clickable”输入文本前等待输入框可见”visible”断言页面标题变化时等待URL改变”url_changed”。补充断言录制时可能遗漏一些断言点。仔细审查每个步骤后的状态添加必要的断言。例如登录后除了跳转还应断言页面出现了用户昵称元素。提取测试数据将硬编码在步骤value字段中的测试数据如用户名、密码提取到外部的data部分并为它们设计有意义的变量名如${data.valid_username}。添加前置和后置条件补充用例执行前的环境准备如清理Cookies、设置浏览器窗口大小和执行后的清理工作。经过这个阶段你的JSON用例就从“可运行”变成了“健壮、可维护”。3.4 第四阶段开发SeleniumBase JSON执行器这是整个方案的“大脑”。我们需要创建一个SeleniumBase测试类它能够读取、解析并执行我们定制格式的JSON文件。核心执行器类示例import json import pytest from seleniumbase import BaseCase class JSONTestCaseRunner(BaseCase): 通用JSON测试用例执行器。 通过pytest.mark.parametrize传入不同的JSON文件路径来运行多个用例。 testcase_json None # 将通过参数传入 def run_testcase(self): 执行JSON测试用例的主方法 if not self.testcase_json: self.fail(未加载测试用例JSON数据) tc self.testcase_json.get(testcase, {}) test_data self.testcase_json.get(data, {}) self.logger.info(f开始执行用例: {tc.get(name)}) # 1. 执行前置条件 for precond in tc.get(preconditions, []): self._execute_step(precond, test_data) # 2. 执行核心测试步骤 for step in tc.get(steps, []): self._execute_step(step, test_data) # 3. 执行后置条件 for postcond in tc.get(postconditions, []): self._execute_step(postcond, test_data) self.logger.info(f用例执行完成: {tc.get(name)}) def _execute_step(self, step, test_data): 执行单个步骤 step_id step.get(step_id, N/A) description step.get(description, ) action step.get(action) locator_info step.get(locator) value self._resolve_value(step.get(value), test_data) # 处理${data.xxx}变量 wait_info step.get(wait, {}) assertion_info step.get(assertion) self.logger.info(f[步骤{step_id}] {description}) # 处理等待 if wait_info: self._apply_wait(locator_info, wait_info) # 执行操作 if action navigate: self.open(value if value else locator_info.get(url, about:blank)) elif action input_text: self.type(self._get_locator(locator_info), value) elif action click: self.click(self._get_locator(locator_info)) elif action select_dropdown: self.select_option_by_text(self._get_locator(locator_info), value) # ... 添加更多操作映射如 clear_text, double_click, hover 等 elif action assert: self._execute_assertion(assertion_info, locator_info) else: self.logger.warning(f未知操作类型: {action}) def _get_locator(self, locator_info): 将locator对象转换为SeleniumBase可用的定位器字符串 if not locator_info: return None strategy locator_info.get(strategy) value locator_info.get(value) # SeleniumBase 支持类似 ”css:#username” 或 ”id:username” 的格式 if strategy css selector: return fcss:{value} elif strategy id: return fid:{value} elif strategy xpath: return fxpath:{value} elif strategy link text: return flink:{value} else: return value # 回退到原始字符串 def _apply_wait(self, locator_info, wait_info): 应用等待策略 wait_type wait_info.get(type, visible) timeout wait_info.get(timeout, 10) locator self._get_locator(locator_info) if wait_type visible and locator: self.wait_for_element_visible(locator, timeouttimeout) elif wait_type clickable and locator: self.wait_for_element_clickable(locator, timeouttimeout) elif wait_type presence and locator: self.wait_for_element_present(locator, timeouttimeout) # ... 支持更多等待类型 def _execute_assertion(self, assertion_info, locator_info): 执行断言 if not assertion_info: return assertion_type assertion_info.get(type) expected assertion_info.get(expected) locator self._get_locator(locator_info) if assertion_type text_equal and locator: actual_text self.get_text(locator) assert actual_text expected, f文本断言失败。预期: {expected}, 实际: {actual_text} elif assertion_type title_is: actual_title self.get_title() assert actual_title expected, f标题断言失败。预期: {expected}, 实际: {actual_title} elif assertion_type url_contains: current_url self.get_current_url() assert expected in current_url, fURL断言失败。预期包含: {expected}, 实际URL: {current_url} elif assertion_type element_present and locator: self.assert_element_present(locator) # ... 支持更多断言类型 def _resolve_value(self, raw_value, test_data): 解析步骤中的值替换${data.xxx}变量 if isinstance(raw_value, str) and raw_value.startswith(${data.) and raw_value.endswith(}): key raw_value[7:-1] # 去掉 ${data. 和 } return test_data.get(key, raw_value) # 如果找不到返回原值 return raw_value # 使用pytest参数化来运行多个JSON用例 import os def list_json_testcases(testcase_dir./testcases): 列出指定目录下所有的JSON测试用例文件 json_files [] for root, dirs, files in os.walk(testcase_dir): for file in files: if file.endswith(.json): full_path os.path.join(root, file) json_files.append(full_path) return json_files # 动态生成测试用例 def pytest_generate_tests(metafunc): if json_file_path in metafunc.fixturenames: testcase_files list_json_testcases() metafunc.parametrize(json_file_path, testcase_files, scopefunction) # 实际的测试函数 def test_json_runner(json_file_path): 加载JSON文件并执行测试 with open(json_file_path, r, encodingutf-8) as f: testcase_data json.load(f) runner JSONTestCaseRunner(test_json_runner) runner.setUp() runner.testcase_json testcase_data try: runner.run_testcase() finally: runner.tearDown()这个执行器是一个基础框架。在实际项目中你需要根据业务操作类型不断丰富_execute_step方法中的操作映射和断言类型。4. 高级技巧与最佳实践方案跑通只是第一步要让它在团队中真正好用、耐用还需要一些“内功心法”。4.1 实现真正的数据驱动测试前面的例子只是将数据内嵌在JSON中。更优雅的做法是将测试数据完全外部化。独立的JSON数据文件创建一个test_data目录里面存放如login_data.json、search_data.json等文件。// test_data/login_data.json [ { username: standard_user, password: secret_sauce, expected_url_part: /inventory }, { username: locked_out_user, password: secret_sauce, expected_url_part: , expected_error: Epic sadface: Sorry, this user has been locked out. } ]修改执行器在pytest_generate_tests中不仅遍历用例文件还遍历对应的数据文件为每个用例生成“用例数据”的组合。这需要你在JSON用例文件中通过一个字段如”data_file”: “login_data.json”来关联数据文件。参数化执行利用pytest强大的pytest.mark.parametrize为每个测试用例动态生成多个测试实例每个实例注入一组不同的测试数据。这样一份操作流JSON加上N份数据JSON就能自动生成N个测试。4.2 元素定位器管理与页面对象模式融合直接在步骤JSON中写定位器当页面元素频繁变更时维护会成为噩梦。我们可以引入一个定位器仓库的概念。创建定位器映射文件一个独立的JSON文件如locators.json以键值对形式存储所有元素的定位信息。{ login_page: { username_input: {strategy: id, value: user-name}, password_input: {strategy: id, value: password}, login_button: {strategy: id, value: login-button}, error_message: {strategy: css selector, value: .error-message-container} }, inventory_page: { product_title: {strategy: css selector, value: .inventory_item_name} } }修改JSON用例步骤中的locator字段不再直接写定位器对象而是写一个引用键如”locator”: “login_page.username_input”。修改执行器在执行_get_locator方法时先从这个全局的定位器仓库中根据键名解析出真正的定位器对象。这样当元素ID变化时你只需要更新locators.json中的一个地方所有引用该元素的测试用例都会自动生效。这其实就是一种轻量级的、基于JSON的“页面对象模型Page Object Model, POM”实现极大地提升了可维护性。4.3 测试报告与结果整合SeleniumBase本身会生成不错的pytest-html报告。但我们的用例是以JSON文件为单位的我们可能希望报告能更直观地反映这一点。自定义报告内容可以在JSONTestCaseRunner类中重写SeleniumBase的日志记录方法将当前执行的JSON用例ID、名称、步骤描述等信息更详细地输出到报告中。结果聚合正如网络资料中提到的我们可以像处理Newman报告一样解析SeleniumBase生成的HTML报告提取结构化数据。然后我们可以编写一个后处理脚本将这些数据与我们运行的JSON用例元信息如用例ID、模块关联起来生成一个更定制化的汇总报告甚至可以整合API测试Newman的结果形成统一的QA质量看板。失败分析与截图SeleniumBase在断言失败时会自动截图。我们需要确保截图名称能关联到具体的JSON用例和失败步骤方便快速定位问题。可以在_execute_step方法中加入try-catch在步骤失败时用包含用例ID和步骤ID的格式保存截图。4.4 持续集成CI集成将这套JSON自动化测试集成到CI/CD流水线如Jenkins、GitLab CI、GitHub Actions中是实现价值最大化的关键。目录结构标准化在代码仓库中建立清晰的目录结构例如automation/ ├── testcases/ # 存放所有JSON测试用例 ├── test_data/ # 存放所有测试数据文件 ├── locators/ # 存放各页面的定位器文件 ├── runner/ # 存放JSON执行器核心代码 ├── requirements.txt # Python依赖 └── pytest.ini # pytest配置文件编写CI配置在CI的配置文件中如.gitlab-ci.yml定义测试任务。stages: - test ui-automation: stage: test image: python:3.9-slim script: - pip install -r automation/requirements.txt - cd automation - python -m pytest runner/test_json_runner.py --htmlreport.html --self-contained-html artifacts: paths: - automation/report.html when: always测试触发策略可以配置为每次代码推送push到特定分支如develop,main时触发或者每晚定时执行确保核心功能的持续回归。5. 常见问题与避坑指南在实际落地过程中你肯定会遇到各种问题。这里分享一些我踩过的坑和解决方案。5.1 元素定位不稳定导致测试“飘”这是UI自动化最常见的问题。JSON方案中定位器写在文件里看似更脆弱。问题根源动态ID或类名。页面加载速度导致元素未及时出现。iframe或Shadow DOM。转换脚本生成的定位器质量差如过长、绝对的XPath。解决方案优先使用唯一且稳定的属性与开发约定为关键测试元素添加>