本文首发自 微盟技术中心 微信公众平台~
一、前言
在前端快速迭代的背景下,自动化测试变的尤为重要,它是质量保证、人力提效的关键。对于 Web 端自动化测试我们常常可见的就是单元测试、集成测试和 UI 测试等。那么对于小程序而言,是否有一整套的自动化测试玩法呢?不仅做到界面的 UI 检查、又能实现端到端完整的功能测试?答案是有的~
接下来,我们将结合当前微盟 CRM 业务场景,从 0->1为 大家介绍如何实现小程序自动化测试。
二、背景****
CRM 是微盟小程序必不可少的业务,在频繁的迭代下如何保证质量更是重中之重。在仅使用人力测试遇到非常多的痛点和局限:
1、本身 CRM 涉及业务非常广泛,会员、积分、礼品卡等等,光是页面就接近50个,仅主流程回归就需要4~5day人力。
2、CRM有非常多的下游依赖,譬如 CMSSDK 涉及的授权能力,当依赖发生变更,需要机械性回归的场景非常多,纯人力去操作不仅效率低效而且会遗漏些关键场景。
3、真实的用户场景可能会遇到网络异常、服务异常等情况,而我们的测试环境很难去模拟这样的灾难测试。譬如 CRM 礼品卡,在网络异常情况下,就会出现页面表现不一致的情形(白屏/内容塌陷/一直loading加载)。
在这样背景下,我们开始了自动化测试的路程~
三、技术选型
明确了痛点后,我们在市面上进行了如下技术选型和对比:
1、 基于 CDP(chrome 远程调试协议)的相关工具
此类工具(playwright、selenium)往往应用在 Web 开发,本质是通过协议去操控页面的行为。
但是对于小程序这样的载体,基于 CDP 协议的工具有以下的缺点:
- 真机兼容性:小程序底层内核多样化,是否能成功连接真机有很大风险。
- 能力受限:基于小程序安全问题,CDP相关工具只能做到 UI 测试,与我们的需求不符。
- 测试平台不足:测试覆盖不够,真机往往是链接的安卓调试,缺少 iOS 真机测试。
实操中,我们无法通过 CDP 的协议去控制小程序 JS 逻辑行为,只能简单的控制页面滚动、点击、跳转等。
此种方案不符合我们真实的需求,我们希望能够获取小程序的逻辑层,进行数据的修改,请求的发起等等。
2、 微信小程序官方SDK: miniprogram automator
miniprogram automator,可以控制小程序页面的行为以及事件。
- 控制小程序跳转到指定页面。
- 获取小程序页面数据。
- 获取小程序页面元素状态。
- 触发小程序元素绑定事件。
- 调用 wx 对象上任意接口。
相比于第一种方案,此方案可以获取到逻辑层初步满足业务场景的覆盖,但是它无法完成小程序的自动授权(地理位置等),这会让我们 CRM 很多业务场景受限。
3、 云测自动化:微信测试团队推出云真机解决方案
云测自动化,由微信测试团队自主研发,联合 WeTest 云真机能力,共同推出的小程序自动化测试服务
- 丰富的自动化测试能力
- 全面的性能分析能力
- 支持持续集成,打通业务 Devops 流程
- 支持第三方服务商
第二、和第三种方案都是比较符合我们的期望,我们进一步进行对比这两种的优劣:
从生态、支持语言、真机配置、测试框架等角度分析,云测作为解决方案是最合适我们的场景,我们无须配置复杂的真机场景,并且可以结合 OpenAPI 去构建专属CRM 业务的自动化测试。
接下来我们具体展开如何基于云测完成我们的自动化测试能力。
四、实现方式
简而言之,使用云测能力跑测我们的业务需要以下步骤:
1、针对各自的业务情况,使用云测提供的miniTest框架去编写测试用例。
2、登录云测平台,可通过开发者工具云测插件进入即可。
3、上传我们第一步的测试用例。
4、指定计划,让云真机按测试用例执行跑测。
1、测试用例的编写
首先,MiniTest 框架本身支持两种语言 Python3/js 去编写用例,这里我们选择了在自动化领域生态更好的 Python。这里为了让大家对此类用例有个基本印象,举个简单的例子,写一个进入 Home 页面中去判断元素"#auth-btn"是否存在的测试用例。
scss
#!/usr/bin/env python3import miniumclass FirstTest(minium.MiniTest): def test_auth_btn(self): self.app.navigate_to("/pages/home") page = self.app.get_current_page() page.element_is_exists("#auth-btn")
MiniTest提供了测试断言以及当前小程序 Page、Data、Element 获取的 API,具体大家可以直接进入文档查看小程序云测-MiniTest。
2、测试用例架构
重中之重是如何去维护、编排我们的测试用例,如果只是零零散散的书写函数是不够支撑整个项目的自动化测试的,我们需要对待业务项目一样去管理我们整套的测试用例,如下是我们营销云业务测试用例架构图:
整个架构分为四层:
- 底层依赖 Python3、MiniTest的执行环境,我们通过 pipenvfile 进行依赖的管理,为了方便大家理解,可以把它类比于 package.json(如下图),定义了源、开发环境依赖、脚本 Scripts 等信息。
lua
# 下载源[[source]]url = "https://pypi.org/simple"# 包依赖[packages]requests = "*"minium = "*"# 开发依赖[dev-packages]# 外部依赖[requires]python_version = "3.9"# 执行脚本[scripts]uploadCase = "python3 ./scripts/uploadCase.py"test = "minitest -s suite.json -c config.json -g"
- 配置层主要用于开发环境下,本地调试使用,通过 suite.json 进行用例执行顺序的编排,config.json 进行项目配置(项目路径、工具路径、端口等等)。
arduino
### config.json{ # 项目路径 "project_path": "/Users/xxx/Desktop/weimobProject/marketingCloudProject/saas-fe-titan-hd/dist", # 开发者工具路径 "dev_tool_path": "/Applications/wechatwebdevtools.app/Contents/MacOS/cli", # 云测token "myToken": "#####", # 云测项目名称 "myProjectName": "####"}
arduino
### suit.json{ # 用例列表 "pkg_list": [ { "case_list": [ "test_*" ], "pkg": "case.testBuyAndSendCard" } ]}
- 测试用例分层,相比于传统的 PO 分层,我们颗粒度更细,将业务拆解出流程process(购买流程、加购流程等等),页面 Page 层,组件 Component 层,通过这几层组合成一个个用例。
- 最顶层就是我们的用例执行层了(回归测试、多面值加购等等),这些执行层本质上就是用例分层组合而来的。举个例子:执行层为回归测试计划(涉及购买流程、赠送流程)
css
// 执行层的回归测试# 购买流程 + 赠送流程 测试用例from case.base.baseCase import BaseCasefrom case.base.basePage import BasePagefrom case.process.giftingProcess import GiftingProcessfrom case.process.buyGiftCardProcess import BuyGiftCardProcessfrom case.base.route import routeclass TestBuyAndSendCard(BaseCase, BasePage): def __init__ (self, methodname='runTest'): super(TestBuyAndSendCard, self).__init__(methodname) self.giftingProcess = GiftingProcess(self) self.buyGiftCardProcess = BuyGiftCardProcess(self) """ case1: 购买流程回归 """ def test_001_testBuyGiftcard(self): self.buyGiftCardProcess.startProcess()
""" case2: 赠送流程回归 """ def test_002_sendFriend(self): self.giftingProcess.startProcess()
我们可以看到,其中依赖了两个process (giftingProcess,buyGiftCardProcess),而process中又包含了具体Page层的测试方法,我们看看buyGiftCardProcess内部长什么样子:
arduino
# 购买流程测试用例from case.base.baseCase import BaseCasefrom case.pages.home import HomePagefrom case.base.basePage import BasePagefrom case.base.route import routefrom case.pages.templateDetail import TemplateDetailimport time"""礼品卡购买流程测试礼品卡首页->礼品卡主题详情页->支付"""class BuyGiftCardProcess(BaseCase, BasePage): def __init__ (self, mini): BasePage.__init__(self,mini) self.homePage = HomePage(mini) self.templateDetail = TemplateDetail(mini) self.cmsAuthPage = CMSAuth(mini) """ 开启流程 组合相关页面方法 """ def startProcess(self): self.navigate_to_open(route["home"]) self.homePage.callAllPageFun() self.templateDetail.callAllPageFun()
我们在 buyGiftCardProcess 中用到了 Page 层中的 home/templateDetail 页面的方法,同理这些page的方法也会用到下一层component暴露的能力。本质上,我们采用的就是组合模式以及命令模式将用例进行编排,这样做的好处就是复用性、拓展性更好,便于迭代和维护。
当然,作为一个完整的架构,我们还需要去约束我们用例编写的规范,举个例子,我们可以使用 Python 抽象类的方式要求每个页面必须实现校验路由、基本元素以及当前页面所有用例执行的调用,让后每个页面注入这样的抽象类进行约束。
ruby
from abc import ABCMeta, abstractmethod# 定义页面抽象类 页面必须实现校验当前路由、校验基本元素、当前页面整体调用链路等方法class StandardPage(metaclass=ABCMeta): @abstractmethod def callAllPageFun(self): pass
@abstractmethod def checkPath(self): pass
@abstractmethod def checkBasicElement(self): pass
我们针对用例中经常出现的方法进行了统一封装,譬如高频点击事件、页面跳转事件、元素获取事件等等。同样,也提供了一些 Script 的能力,帮助编写用例人员能自动化创建用例模板、自动化去压缩并上传整个用例,下面是整个项目的文件目录:
ruby
├── Pipfile // 类似于package.json├── case│ ├── base│ │ ├── baseCase.py // 测试用例的生命周期钩子│ │ ├── basePage.py // 用例中常用的方法封装│ │ ├── route.py // 路由文件│ │ └── standardPage.py // 抽象类约束页面方法│ ├── pages // 页面维度暴露测试方法│ │ ├── components // 通用组件维度暴露测试方法│ │ ├── exampleDetail.py│ └── process // 流程维度暴露测试方法│ └── exampleProcess.py│ ├── example.py // 用例执行文件├── config.json // 本地测试配置文件├── debug.ipynb // 本地调试脚本├── env // 运用在debug.ipynb 用于debug时候统一环境、统一方法├── outputs // 本地日志输出├── scripts // 脚本能力├── suite.json // 本地用例编排,按这个文件执行测试用例
这样架构设计的目的是因为现代化的前端应用颗粒度非常细,涉及业务场景也很多,我们需要更合理的用例分层去应对。
相比于传统测试用例的编写,我们的优势在于以下四点:
- 现代化的分层设计
- 规范化的用例约束
- 统一依赖管理
- 本地调试能力
- 自动化的快捷能力
通过这样的架构设计,我们的测试团队能够快速且规范的编写用例,CRM 测试团队投入一人只需要两周时间就能够完成50个页面的基本 UI 测试。
3、CRM落地业务效果展示
目前在CRM中,礼品卡、会员积分和用户中心等业务都采用以上的框架进行用例的编排,我们对礼品卡进行了回归流程的跑测和灾难模拟的跑测:
(自动化跑测回归流程)
如上图的主流程回归是通过购买业务、赠送业务以及绑卡业务层组合构成,每个业务流程由页面层暴露的校验方法构成,而页面层进一步由整个项目中组件层构成。这样灵活的设计组合,可以让我们应对多变的业务场景,拓展性更高。
(自动化灾难模拟)
如上图,我们模拟了当礼品卡服务挂了之后,页面的展示情况,通过这样的模拟测试,我们很容易发现页面的一些问题,譬如白屏、一直 Loading、内容塌陷等等,这样一来我们可以针对性的对用户体验进行优化。
模拟当前服务挂了的代码也是比较简单的,这里举个简单的例子:
lua
#!/usr/bin/env python3import miniumclass RequestTest(minium.MiniTest): def test_mock_request(self): self.app.restore_request() # 清空规则 mock_resp1 = {"errmsg": "服务暂不能使用", "statusCode": 500} rule1 = ".*/SendMsg\?.*" url1 = "http://minitest.weixin.qq.com/SendMsg?content=test" # 加入规则1 self.app.mock_request(rule1, fail=mock_resp1) result = self.app.call_wx_method("request", [{"url": url1}]).get("result", {}).get("result") self.assertDictEqual(result, mock_resp1) # 返回服务挂掉
CRM云测报告展示:
4、 mini-cli提效
以上的内容就可以完成一整套的自动化测试,想必大家已经跃跃欲试了,但是实操过程中会有各种问题,譬如环境如何配置、测试报告如何通知以及开发环境下如何自测等等问题,所以我们研发了 mini-cli 工具去帮助各个业务方。
4.1 快速搭建
CRM 涉及多个业务,如果每个业务都从0->1去搭建是比较麻烦的。我们通过 mini-cli create 的命令能够快速生成上述测试用例框架,相比于人工搭建提升了90%效率,降低了使用门槛和减少了人力成本。
同时 mini-cli 提供了快速创建模版页面的能力,mini-cli initPage 可以读取路由文件,为我们批量生成测试用例模板,模版中已经有基础的测试方法,测试人员在对应位置写入用例即可。
4.2 集成到开发阶段****
在实际的开发中,开发人员的提测质量、冒烟通过率往往会影响到测试的进度,基于此痛点,我们希望自动化测试不仅用于测试阶段,还能够在开发阶段嵌入,让开发人员每一次发布都能够完成主流程的回归。mini-cli 将开发->自动化测试->企微通知->模块发布进行打通,如下图:
a) 关键能力-mini.config.js
工具在设计层面需要兼顾到不同的业务方,设计模式上要符合开放封闭原则,所以我们提供了一个配置型文件mini.config.js。工具会根据配置文件进行打包上传、测试任务、判断分支能力、组装数据以及发布。
ruby
* appid // 小程序appid* projectPath // 本地项目路径(小程序dist包)* privateKeyPath // 私钥路径 参考下方文档路径* myToken // 自动化测试令牌* myProjectName // 云测平台下项目英文名称* QW_KEY // 企微webhook* canPublishBranchList // 允许运行自动化测试的分支 默认都允许 否则string[]* QW_reportInfo // 企微报告信息配置 planName -- 测试名称 desc -- 测试描述* lowestPublishRate // 最低发布通过率* taskID // 需执行的测试任务
1、QW_KEY 是用于后续将测试报告发送到企微机器人中,让开发、测试及时获取到跑测信息。
2、canPublishBranchList 用于校验当前分支是否允许发布开发版、是否允许自动化测试。
3、QW_reportInfo 允许业务方自定义一些测试信息用于测试报告通知使用。
4、lowestPublishRate 业务方可以自定义最低的测试通过率,当大于这个数字,会自动使用titan-cli发布当前包用于提测。
5、taskID 业务方云测的任务ID,若不填,会默认拉取所有测试任务列表,开发者在命令行自行选择。
b) 关键能力-通知云测跑开发版
我们遇到的第一个问题就是当开发者完成迭代后,如何让云测知悉要跑测当前最新的代码?
解决方案是使用 miniprogram-ci 能力去提交代码到最新的开发版,并且通过请求云测的 API 能力唤起自动化测试。这块能力集成到了mini-cli run test 中,开发人员只需执行即可,无须感知内部逻辑。
c) 关键能力-监听测试结果
自动化测试的费时一般在20分钟左右,由于云测不提供任何回调能力,我们无法及时的拿到测试的结果,所以我们采取的方案是轮询请求,每间隔十分钟会自动请求云测的测试状态,当拿到了测试结果,mini-cli会进行组装测试数据和报告,并通过企微API的能力发送报告,及时告知开发人员测试结果。
d) 关键能力-自动化测试与发布能力的结合****
当自动化测试通过最低发布通过率时,会自动唤起发布工具的能力,但是发布工具titan会唤起命令行交互窗口,相当于中断了整个过程,需要开发者手动进行选择才能继续下一步操作,这样的体验并不好,因此我们的研发人员支持了默认参数的方式,通过默认参数配置让发布工具不再唤起交互窗口。这样mini-cli的自动化测试能力和发布就完美的结合了,开发人员无须感知任何跑测和包发布的过程。本质上mini-cli是将miniprogram-ci能力、云测API能力、企微通知能力以及titan-cli(wos小程序模块发布能力)集成到了一起,这样做可以让开发人员在发布前就可以完成自测。
五、总结
本文介绍了 CRM 自动化测试的架构设计,基于这样的架构下,CRM完成了50多个页面的 UI 测试,以及会员卡、礼品卡等多个业务场景的主流程回归。回归的功能点多达上百个,大大提高了测试效率,主流程的回归由原本的5day降到了1day,提升了80%的能效。另一个方面,灾难模拟测试暴露出小程序在边界情况下的问题,继而我们优化了页面的布局、不合理的 Loading 加载以及增加了骨架屏,给用户带来更好的体验。
通过 mini-cli 工具,CRM 开发人员自测阶段平均能发现1-2个问题,冒烟和提测的通过率接近100%,让 CRM 整体的研发质量得到进一步提升。
六、参考资料
1.云测平台官网
小程序云测-MiniTest(minitest.weixin.qq.com/#/minium/Py...
2.CDP协议介绍
Chrome DevTools Protocol(chromedevtools.github.io/devtools-pr...
3.小程序自动化介绍
小程序自动化 | 微信开放文档(developers.weixin.qq.com/miniprogram...