游乐游手机版
首页/AI教程/文章详情

开源通用测试框架实现Web/App/接口统一技能调用

时间:2026-06-09 15:34
提出一套通用测试Skills框架,通过技能抽象、注册中心和动态调度,实现Web、App及接口测试的统一技能调用。该框架解耦底层工具,使同一用例可跨端运行,减少重复代码,降低新人学习成本,提升测试资产复用性。

一、测试团队越做越累,不是人不够,是技能太散

上个月,一个中型电商团队找到我做技术评审。他们有三个测试小组:Web、App、接口。Web组用Playwright,App组用Appium,接口组用Requests + Pytest。三个组,三套代码仓库,三种定位器写法,三种等待策略。新人进来,要先学三套东西。一个跨端场景——比如从Web下单,App确认收货——要三个组各写一遍,再用消息队列串起来。

他们来问:要不要裁掉一组人,或者统一用某个商业平台?

答案很简单:问题不在人,在你们的技能没有统一抽象。

每一端都在做类似的事:点击、输入、获取文本、等待条件、发送请求、断言响应。但每个框架都用自己的方式表达这些“技能”。Web的“点击”是page.click(locator),App的“点击”是element.click(),接口的“请求”是requests.post(url, data)。本质上,它们都是“执行一个动作并验证结果”。但你们的代码里,每一层都在重复实现调度、重试、日志、断言。这不是技术债,这是架构债。

花了三周时间,把过去几年在多个项目里积累的经验抽出来,做了一个通用测试Skills框架。核心目标很简单:一套技能描述,同时驱动Web、App、接口。统一调用方式,统一技能注册,统一结果断言。代码已经开源。下面讲清楚它怎么工作。

二、本质不是缺框架,是缺“统一调用层”

很多人一听到“统一框架”,第一反应是再做一套超级框架,把所有底层都包进去。那是错误的思路。正确的思路是:不要在底层统一,要在“技能调用层”统一。

什么是技能?技能是一个可命名的、有输入输出、有执行逻辑的最小测试单元。比如:

  • click(selector) 是一个技能
  • input_text(selector, text) 是一个技能
  • http_get(url) 是一个技能
  • wait_for_element(selector, timeout) 是一个技能
  • assert_text_contains(text) 是一个技能

Web端需要这些技能,App端也需要,接口端需要的只是其中一部分。关键在于:技能的调用方不关心技能背后是Playwright、Appium还是Requests。它只关心技能的名字和参数。这就好比你在写业务代码时调用一个函数,你不管这个函数是用Go写的还是Python写的。所以我们需要的不是统一的执行引擎,而是统一的技能注册表 + 动态调度器。这个框架干的就是这件事。

三、核心机制拆解:Skill抽象 + 注册中心 + 动态调度

先看架构图。

拆解三个核心机制。

机制一:Skill定义规范 - 让每个技能自描述

一个Skill的最小定义:

@register_skill("click")
def skill_click(selector: str, timeout: int = 5, **context):
    """点击指定元素,支持Web/App统一selector"""
    driver = context["driver"]  # 由调度器注入
    # driver可能是Playwright的Page,也可能是Appium的WebElement
    driver.click(selector, timeout=timeout)

但这样还不够。每个底层驱动的API不同。所以真正的Skill实现是一个适配器:

class ClickSkill(BaseSkill):
    name = "click"
    parameters = {"selector": str, "timeout": int}
    def execute(self, params, context):
        driver = context["driver"]
        if driver.__class__.__name__.startswith("Playwright"):
            driver.locator(params["selector"]).click(timeout=params["timeout"])
        elif driver.__class__.__name__.startswith("Appium"):
            driver.find_element(AppiumBy.XPATH, params["selector"]).click()
        # 接口层不支持click,调用会报错

关键点:技能内部知道当前driver是什么类型,自己做适配。调用方完全不用管。

机制二:注册中心 - 技能的市场

所有技能启动时注册到中心。注册表维护一个字典:skill_name -> SkillClass。调度器收到调用请求后,去注册表找技能,实例化,调用execute。好处:新增技能不需要修改调度器代码。团队可以共享技能库,比如login_with_retrywait_for_toast

机制三:动态调度 - 一套DSL跑通所有端

调度器接受两种输入:

  1. YAML/JSON序列:适合关键字驱动
  2. Python链式调用:适合代码风格

一个YAML用例示例:

name: 跨端下单流程
skills:
  - name: na vigate
    params: {url: "https://xxx.com"}
  - name: click
    params: {selector: "#add-to-cart"}
  - name: wait_for_element
    params: {selector: ".cart-badge", timeout: 5}
  - name: http_post
    params: {url: "/api/checkout", data: {"item_id": 123}}
  - name: assert_status_code
    params: {expected: 200}

这个用例可以在Web环境跑(na vigate, click),也可以在纯接口环境跑(http_post, assert_status_code)。调度器会根据当前注册的技能集合,跳过不可用的技能(如click在接口环境自动跳过并报警)。

核心设计哲学:技能是原子能力,用例是技能的有序组合。底层驱动可以换,技能可以增删,但用例结构不变。

四、典型案例对比:同一个场景,三种终端,一套写法

拿“登录并校验”这个场景举例。

传统方式:三套代码

Web端:

page.goto("/login")
page.fill("#username", "test")
page.fill("#password", "pass")
page.click("button")
page.wait_for_selector(".welcome")
assert page.text_content(".welcome") == "欢迎"

App端(类似,但API不同):

driver.find_element(By.ID, "username").send_keys("test")
driver.find_element(By.ID, "password").send_keys("pass")
driver.find_element(By.ID, "login_btn").click()
wait = WebDriverWait(driver, 5)
wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "welcome")))
assert driver.find_element(By.CLASS_NAME, "welcome").text == "欢迎"

接口端:

resp = requests.post("/login", json={"user": "test", "pwd": "pass"})
assert resp.status_code == 200
assert "欢迎" in resp.text

Skills框架方式:一套用例

skills:
  - name: na vigate
    params: {url: "/login"}
  - name: input_text
    params: {selector: "#username", text: "test"}
  - name: input_text
    params: {selector: "#password", text: "pass"}
  - name: click
    params: {selector: "button"}
  - name: wait_for_element
    params: {selector: ".welcome", timeout: 5}
  - name: assert_text
    params: {selector: ".welcome", expected: "欢迎"}

把这个YAML丢给调度器,设置driver_type=web,跑Web。设置driver_type=app,跑App(只要selector能被Appium解析)。设置driver_type=api,框架会自动将input_text和click转换为HTTP请求(如果实现了对应映射)。实际上,接口环境不需要填表单,所以我们会为接口场景单独写一个更简洁的技能序列。但关键在于:测试人员不需要记住三套API,只需要记住技能名字和参数。

可以被截图传播的观点句:

五、工程落地启示:你的测试资产不该绑定在某种工具上

这个框架开源后,已经在三个团队落地。总结三条最直接的经验。

启示一:把现有测试脚本拆成“技能库”和“用例层”

不要一次性重写所有用例。先从最常用的10个操作开始,注册成技能。然后让用例通过技能调用来重构。三个月后,你的用例文件会减少70%的重复代码。

启示二:技能可以跨项目共享

我们在框架里内置了一个远程技能仓库。团队A写的captcha_solver技能,团队B可以直接拉下来用。不需要复制代码,不需要知道内部实现。这对中大型团队的价值极大:你不再需要每个项目都配一个“自动化专家”。

启示三:新人培训周期从两周压缩到两天

新人只需要学会技能列表和YAML写法。不需要先学Playwright API,再学Appium,再学Requests。他们可以在第一天就写出能跑的用例,第二天理解技能背后的原理。

对在校生来说:你现在不需要纠结“学Web自动化还是App自动化”。你应该学的是“如何抽象测试技能”。这个能力在任何端都通用。

对初级工程师来说:试着把你平时写的Playwright脚本,重构为技能+用例的形式。你会发现自己开始从“写代码的人”变成“设计框架的人”。

对中级工程师来说:这个框架展示了如何用“注册中心+适配器”模式解耦测试工具。你可以把它推广到你的团队,或者自己实现一个更轻量的版本。

六、问你的团队一个问题

去你团队的自动化代码仓库里,随便找一个跨端的场景(比如用户从注册到下单)。统计一下:为了支持Web、App、接口三种环境,你的代码里重复实现了多少遍“等待元素出现”“输入文本”“点击按钮”?然后问自己:如果明天要换掉其中一个底层框架(比如从Playwright换成Cypress),你要改多少处代码?

如果答案是“超过10处”,那么这个框架就值得你花一天时间研究。

来源:https://bbs.huaweicloud.com/blogs/478854
上一篇外贸B2B如何评估AI搜索的GEO可观测性 下一篇智慧工地云平台集成AI技术的实践方法
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
CapCut AI Docker 一键部署:镜像拉取、端口映射与数据目录配置教程
AI教程 · 2026-06-30

CapCut AI Docker 一键部署:镜像拉取、端口映射与数据目录配置教程

CapCutAI容器化部署需先确认镜像来源与授权范围,再完成环境准备、镜像拉取、端口映射、数据目录挂载和启动验证,适合本地试用、团队内网演示与轻量化AI剪辑服务管理。

CapCut AI Windows本地安装配置2026最新版含下载与环境要求
AI教程 · 2026-06-30

CapCut AI Windows本地安装配置2026最新版含下载与环境要求

CapCutAI与剪映AI在Windows端适合短视频、口播、课程和营销素材剪辑,安装前需确认系统、显卡、存储与网络条件,优先选择官方渠道下载,并完成账号、素材目录、硬件加速和导出参数配置。

Veo新手保姆级安装教程:从下载到首次运行
AI教程 · 2026-06-30

Veo新手保姆级安装教程:从下载到首次运行

Veo适合用文字生成短视频,新手应先确认官方入口、准备账号与设备环境,再按网页或应用方式完成启用。首次运行重点在提示词、参数、素材合规与结果保存,避免使用非官方安装包。

Veo本地模型运行下载路径设置与性能优化指南
AI教程 · 2026-06-30

Veo本地模型运行下载路径设置与性能优化指南

Veo本地模型部署需先确认模型来源与硬件条件,再完成下载校验、目录规划、路径配置和推理参数优化。重点关注显存占用、依赖版本、缓存位置、授权范围与常见报错处理。

Veo安装失败解决指南:常见报错与日志排查及升级回滚方案
AI教程 · 2026-06-30

Veo安装失败解决指南:常见报错与日志排查及升级回滚方案

Veo安装失败通常与系统环境、依赖版本、网络源、权限和缓存有关。排查时应先确认版本要求,再查看安装日志,按报错类型处理,并提前备份项目,确保升级与回滚可控。