暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

快应用自动化测试初探

AggrxTech 2021-07-17
875
快应用是国内主流手机厂商共同推出的应用生态。开发者低成本接入、快速发布自己的应用,用户无需下载即可使用。目前,随着各厂商对快应用产品的大力扶持,越来越多的产品开始了快应用方向的探索。

提出问题

  与原生APP相同,在快应用的版本迭代中,我们仍然需要耗费大成本对原有功能进行回归,而某些功能往往变化不大,操作和执行结果也相对固定。在原生项目中,我们用UI自动化来解决此类问题,在快应用中,我们仍然希望以自动化测试的方式对既有功能进行快速、低成本的回归测试,以节约人力成本、提高团队效率。那么快应用的自动化测试是怎么样的呢?与原生应用有什么不同呢?接下来,我们就来初步探索一下快应用的自动化测试。
  虽然快应用的自动化测试大家可能有些陌生,但是原生APP的自动化是再熟悉不过了。虽然测试框架众多,但基本思路一致:通过id、xpath等获取控件,完成对应操作,最后验证操作结果。类比原生APP,我们尝试将这个思路套用到快应用的自动化测试上。

知识准备

  快应用官方文档上其实对自动化测试做过简单的阐述https://doc.quickapp.cn/tutorial/others/testing.html,虽然内容不多,但我们从中可以看出基本的测试框架和需要的技术储备:

1. 快应用使用前端技术开发,所以我们需要了解HTMLJavascript代码;
2. 熟悉快应用项目的目录结构和基本代码框架;

3. 测试框架层面,建议熟悉一下Mocha,我们实际上是用单元测试的方式,
触发快应用的UI自动化测试。

测试思路

  刚刚提到快应用使用前端技术开发,涉及JS、UI页面,大家很容易想到E2E测试,即端到端测试。它是页面级别的测试,测试人员通过模拟用户的行为,让页面UI产生响应变化、触发一些事件或方法,进而校验页面和功能是否正确。
  对于快应用的自动化测试采用的正是这种思想。我们借助一些比如mocha、chai等JS库来触发快应用运行、对结果进行断言,进而完成相应的测试任务。

快应用自动化实现

环境搭建

  与原生应用自动化开发不同,快应用的自动化测试与其本身代码密切相关,所以无论是进行开发还是调试,我们都需要搭建完整的开发环境。快应用本身的开发环境搭建请参考官网,在测试中我们会用到mocha库,这里可以一并安装。

npm install --global mocha

这里是进行的全局安装,也可以只针对某一项目安装。


项目目录

  为了便于理解,我们先创建一个结构简单的demo,创建一个名为quickapp-test-demo的快应用项目,

npx hap init quickapp-test-demo

项目中已经创建了一些文件,这里我们主要关注src目录。

|-- quickapp-test-demo
|-- src
| |-- app.ux
| |-- config-phone.json
| |-- global.js
| |-- manifest.json
| |-- sitemap.json
| |-- CardDemo
| | |-- index.ux
| |-- Common
| | |-- logo.png
| |-- Demo
| | |-- index.ux
|-- DemoDetail
|-- index.ux

  快应用的自动化测试是以页面划分的,所以我们需要根据页面创建测试脚本。首先,在src同级目录下创建test目录,再在各页面index.ux文件对应的位置创建该页面的测试文件,创建完毕后目录如下:

|-- quickapp-test-demo
|-- test
|-- autocase.js
|-- Demo
| |-- index.js
|-- DemoDetail
|-- index.js

添加汇总页面

  为了便于调试,增加一个测试汇总页面,并且作为快应用的入口页面。具体步骤可以参照官网文档。页面如下:

一个简单的用例

  在test/Demo/index.js中添加如下代码:

export default function(vm) {
it("Demo Case", function() {
expect(2).to.equal(3)
})
}


打包

  在pacakge.json的script模块中加入测试打包的定义:

"build:test": "npm run build -- --enable-e2e"

这样我们就可以通过执行npm run build:test
,进行测试编译和打包。


执行

  打包后在手机端运行,首先打开的是我们的测试汇总页面,点击Demo,进入Demo页即触发执行测试用例,然后退出。

用例执行后,在console的日志中,我们可以看到测试结果,并且会打印用例的报错信息,我们的测试用例是断言2等于3,显然是不正确的。报错信息如下:

"stack": "AssertionError: expected 2 to equal 3
apt Context.<anonymous> (Demo/index.js:192:18
at callFn (app.js:15516:27)
at Test.Runnable.run (app.js:15509:13)
at Runner.runTest (app.js:15996:16)
at app.js:16107:18
at next (app.js:15909:20)
at app.js:15919:13
at next (app.js:15849:20)
at app.js:15886:11
at Object.timeslice (app.js:10913:33)"

  到此,一个简单的快应用自动化测试脚本就开发并执行完毕了。特别的,如果测试执行后不希望自动退出的话,可以在页面router方法的参数中加上back:false,如图DemoDetail页面:

{
name: 'DemoDetail',
uri: 'DemoDetail',
params: {
back: false
}
}


实现原理

  与快应用通常的打包方式相比,自动化测试的打包增加了[--enable-e2e]参数,开启e2e测试能力,允许开发和测试人员在运行快应用时进行端到端的测试。
  在这里,我们通过两种打包命令,对比生成的build目录中的文件,来看一下[--enable-e2e]参数究竟做了哪些事情。


build/app.js

  src/app.ux文件一般用来引入公共脚本,暴露一些公共方法,其编译后对应的是build目录下的app.js文件。对比两次编译,最直观的差别:带e2e参数编译后,app.js文件代码增加了24000多行。详细看一下,发现带[--enable-e2e]参数打包时,需要引入hybrid-mocha和hybrid-chai两个库,它们是对mochajs和chaijs两个库的封装:

Mocha:诞生于2011年,是一套比较成熟的Javascript单测框架,可以在Node和浏览器环境下使用。有丰富的文档和社区;
Chai:是一个可以在Node和浏览器环境运行的断言库,可以与任何js测试框架结合。

  在这里特别引入了两个全局方法loadData和saveData,利用这两个方法我们可以向js内存中读取或写入数据。


build/Demo/index.js

  我们以Demo为例,看一下页面的变化。build/Demo/index.js是页面文件src/Demo/index.ux编译后对应的文件。我们关注的是增加了下面两个部分:我们刚刚写的测试代码和自动添加的调用执行测试代码的方法。

/*******测试代码**********/
/***/ "./test/Demo/index.js":
/*!****************************!*\
!*** ./test/Demo/index.js ***!
\****************************/
/***/ ((__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.default = _default;

function _default(vm) {
it("Demo Case", function () {
expect(2).to.equal(3)
});
}
/***/ })
/******/ });
......省略多行......

/*******调用代码**********/
__webpack_require__.g.CASE_TEST_START = __webpack_require__.g.CASE_TEST_START || 1000;
__webpack_require__.g.CASE_TEST_TIMEOUT = __webpack_require__.g.CASE_TEST_TIMEOUT || 2000;
__webpack_require__.g.mocha = new Mocha({
reporter: 'json',
timeout: __webpack_require__.g.CASE_TEST_TIMEOUT
});
mocha.ui('bdd');
mocha.suite.emit('pre-require', __webpack_require__.g, null, mocha);
setTimeout(() => {
typeof _index.default === 'function' && (0, _index.default)(this);
const mochaRunner = mocha.run(() => {
if (mochaRunner) {
mochaRunner.testResults.stats.title = mocha.suite.suites && mocha.suite.suites[0] && mocha.suite.suites[0].title;
console.info('testResults: ', JSON.stringify(mochaRunner.testResults));
pushData('pageTestList', mochaRunner.testResults);
const stats = mochaRunner.testResults.stats;
this.$page.setTitleBar({
text: '通过/全部: ' + stats.passes + '/' + stats.tests
});
}

if (this.back !== 'false') {
console.info('拥有关联测试用例,测试完毕,返回到之前的页面');
history.back();
}
});
}, __webpack_require__.g.CASE_TEST_START);

这样就保证了一进入页面,我们的测试代码即被触发执行。


页面操作

让我们回忆一下原生APP的自动化测试,执行一条测试用例,我们大体上要做如下几件事:

  • 获取元素

  • 执行操作:

  • 点击、滑动等验证结果

我们这篇文章主要说说前面两点。


获取元素

  快应用在手机上显示出来的,实际上已经是原生控件了,和我们的代码存在一层映射关系,并不直接。因此在源码中,我们获取元素,只能是Dom元素。那么我们如何才能获取元素呢。观察测试方法,有一个参数vm,我们直接在测试代码中加入console.log(vm),将vm打印出来。结果如图:

为了观察到更多内容,这里我们换了一个比较复杂的页面,可以看到vm中有很多属性和方法,这些都可以通过vm访问到。比如我们要获取页面的某一UI元素,那么我们先将vm.$option展开:

template是此页面Dom元素的根节点,展开template,当我们看到有children属性时,相信你已经Get了所有。比如我们想获取my这个标签的id属性的话就可以通过:

vm.$options.template.children[0].children[2].attr.id

注意分清数组和字典类型的结构。能够获取元素和元素属性,我们就可以对页面控件有无、属性值是否正确进行判断。


执行操作

  原生APP上的功能,可以通过点击等操作触发执行。对于快应用,需要触发事先在Dom元素上定义好的事件,比如用户点击页面元素,触发了其onclick事件,而针对这一事件,开发者指定了响应方法。最终通过执行这一方法实现对应的功能。仍然是看vm参数,可以看到我们定义的所有方法实际上都在vm下。

这里我们可以省去事件触发的流程,直接调用处理方法,来完成页面功能。例如我们调用switchtabfun,用vm.switchtabfun就可以访问到。但是,需要传递参数。Javascript的代码中看不出参数类型,所以这里需要我们看一下switchtabfun的实现:

switchtabfun(e) {
// 页面切换tab
let index = e.detail.index
......

与参数无关的代码已经隐去,方法内部用到的就是e.detail.index,所以我们其实只要造一个参数传入即可。当然我们需要搞清楚index的含义,然后传入不同的值作为正常或异常用例。对于此switchtabfun方法,我们的调用可以写作:

vm.switchtabfun({detail: {'index': 2}})

  需要特别说明的是在一个页面内,引用其他文件定义的ui元素时,直接用vm是无法调到子页面内定义的方法的。如图所示页面:

类似原生fragment布局,当前页面仅仅包含了底部tab和整个容器,中间的内容都是子页面定义的。我们无法直接调用浏览记录入口的点击方法。这里我们仍然看vm,当页面停留在这里时,可以注意到vm._childrenVms中有两个子元素,非常明显的看出一个是容器页面vm,一个是子页面vm,如图:

所以我们展开type为“my”的子vm,可以看到子页面中定义的元素和方法:

很明显的有个方法叫做goReadLog,于是我们用vm._childreVms[1].goReadLog()方式来调用这个方法,果然页面切换到了浏览历史页面。

与原生APP的自动化测试相同,如果页面切换执行后立刻调用goReadLog,会出现调不到的情况,因为此时代码逻辑虽然执行完了,但是UI页面并没有实际切过来。我们仍然需要等一等页面。可以直接调用JS的setTimeout方法,以如下方式延迟5秒后再调用:

setTimeout(function () {vm._childrenVms[1].goReadLog()}, 5000)


结果校验

  校验方式有很多种。可以用现成的断言,也可以自己写逻辑判断,包括某一元素是否存在,其属性是否正确。这里不再详细说明。


总结

  快应用的自动化测试相对原生应用的自动化测试来说,对测试人员的代码熟悉度要求更高,除了JS本身的知识外,还需要对快应用的一些规则有所了解。测试用例均是通过单测的方法,驱动页面功能,进而达到动态测试的效果。
  我写在这里的,也仅仅是初步的探索,有了这些知识,相信大家可以对快应用做初步的自动化开发了。但要想达到原生应用自动化的成熟度,还有很多东西需要探索,大家共勉。


文章转载自AggrxTech,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论