爆肝3周,开源一套通用测试Skills框架:支持Web/App/接口统一技能调用
目录一、测试团队越做越累不是人不够是技能太散二、本质不是缺框架是缺“统一调用层”三、核心机制拆解Skill抽象 注册中心 动态调度四、典型案例对比同一个场景三种终端一套写法五、工程落地启示你的测试资产不该绑定在某种工具上六、问你的团队一个问题一、测试团队越做越累不是人不够是技能太散上个月我帮一个中型电商团队做技术评审。他们有三个测试小组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, timeouttimeout)但这样还不够。每个底层驱动的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(timeoutparams[timeout]) elif driver.__class__.__name__.startswith(Appium): driver.find_element(AppiumBy.XPATH, params[selector]).click() # 接口层不支持click调用会报错关键点技能内部知道当前driver是什么类型自己做适配。调用方完全不用管。机制二注册中心 - 技能的市场所有技能启动时注册到中心。注册表维护一个字典skill_name - SkillClass。调度器收到调用请求后去注册表找技能实例化调用execute。好处新增技能不需要修改调度器代码。团队可以共享技能库比如login_with_retry、wait_for_toast。机制三动态调度 - 一套DSL跑通所有端调度器接受两种输入YAML/JSON序列适合关键字驱动Python链式调用适合代码风格一个YAML用例示例name: 跨端下单流程 skills: -name:navigate 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环境跑navigate, 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.textSkills框架方式一套用例skills: -name:navigate 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_typeweb跑Web。设置driver_typeapp跑App只要selector能被Appium解析。设置driver_typeapi框架会自动将input_text和click转换为HTTP请求如果实现了对应映射。实际上接口环境不需要填表单所以我们会为接口场景单独写一个更简洁的技能序列。但关键在于测试人员不需要记住三套API只需要记住技能名字和参数。可以被截图传播的观点句测试框架的复杂度应该由框架本身承担而不是让每个用例编写者重复学习。技能统一才是真正的资产复用。否则你只是在不同的端里重复造轮子。五、工程落地启示你的测试资产不该绑定在某种工具上这个框架开源后我已经在三个团队落地。总结三条最直接的经验。启示一把现有测试脚本拆成“技能库”和“用例层”不要一次性重写所有用例。先从最常用的10个操作开始注册成技能。然后让用例通过技能调用来重构。三个月后你的用例文件会减少70%的重复代码。启示二技能可以跨项目共享我们在框架里内置了一个远程技能仓库。团队A写的captcha_solver技能团队B可以直接拉下来用。不需要复制代码不需要知道内部实现。这对中大型团队的价值极大你不再需要每个项目都配一个“自动化专家”。启示三新人培训周期从两周压缩到两天新人只需要学会技能列表和YAML写法。不需要先学Playwright API再学Appium再学Requests。他们可以在第一天就写出能跑的用例第二天理解技能背后的原理。对在校生来说你现在不需要纠结“学Web自动化还是App自动化”。你应该学的是“如何抽象测试技能”。这个能力在任何端都通用。对初级工程师来说试着把你平时写的Playwright脚本重构为技能用例的形式。你会发现自己开始从“写代码的人”变成“设计框架的人”。对中级工程师来说这个框架展示了如何用“注册中心适配器”模式解耦测试工具。你可以把它推广到你的团队或者自己实现一个更轻量的版本。六、问你的团队一个问题去你团队的自动化代码仓库里随便找一个跨端的场景比如用户从注册到下单。统计一下为了支持Web、App、接口三种环境你的代码里重复实现了多少遍“等待元素出现”“输入文本”“点击按钮”然后问自己如果明天要换掉其中一个底层框架比如从Playwright换成Cypress你要改多少处代码如果答案是“超过10处”那么这个框架就值得你花一天时间研究。本文部分内容参考了霍格沃兹测试开发学社整理的相关技术资料主要涉及软件测试、自动化测试、测试开发及 AI 测试等内容侧重测试实践、工具应用与工程经验整理。