CRM小程序自动化测试的最佳实践

本文首发自 微盟技术中心 微信公众平台~

一、前言

在前端快速迭代的背景下,自动化测试变的尤为重要,它是质量保证、人力提效的关键。对于 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...

相关推荐
速盾cdn22 分钟前
速盾:网页游戏部署高防服务器有什么优势?
服务器·前端·web安全
小白求学124 分钟前
CSS浮动
前端·css·css3
什么鬼昵称25 分钟前
Pikachu-csrf-CSRF(POST)
前端·csrf
golitter.42 分钟前
Vue组件库Element-ui
前端·vue.js·ui
儒雅的烤地瓜1 小时前
JS | JS中判断数组的6种方法,你知道几个?
javascript·instanceof·判断数组·数组方法·isarray·isprototypeof
道爷我悟了1 小时前
Vue入门-指令学习-v-on
javascript·vue.js·学习
27669582921 小时前
京东e卡滑块 分析
java·javascript·python·node.js·go·滑块·京东
golitter.1 小时前
Ajax和axios简单用法
前端·ajax·okhttp
PleaSure乐事1 小时前
【Node.js】内置模块FileSystem的保姆级入门讲解
javascript·node.js·es6·filesystem
雷特IT1 小时前
Uncaught TypeError: 0 is not a function的解决方法
前端·javascript