测试工具集
小程序的运行环境比较特殊,不同于常见的浏览器环境,它采用的是双线程的架构。而在进行单元测试时,我们并不需要用到这样复杂的架构带来的利好,我们进行的是功能测试而无需苛求性能、安全等因素,因此微信小程序官方提供了一个测试工具集: miniprogram-simulate,以支持自定义组件在 nodejs 单线程中也能运行起来 。
架构图
使用流程
1.安装:miniprogram-simulate需结合jest测试框架一起使用。
jest官网:配置 Jest
miniprogram-simulate:miniprogram-simulate官网
// 小程序工具集$ npm i --save-dev miniprogram-simulate
// Jest测试框架$ npm i --save-dev jest
2. 添加测试脚本命令,在package.json中添加脚本
“scripts”: { “test”: “jest” },
3 . 编写组件测试(演示小demo,更多用法请见上述官网)
a. 在项目新建test文件夹用于存放单元测试文件,创建index.test.js文件,下面就将在此文件编写测试demo。
const simulate = require('miniprogram-simulate')
test('components/index', () => {
const id = simulate.load('/components/index') // 此处必须传入待测试组件绝对路径
// const id = simulate.load(path.join(__dirname, './index')); // 可以视情况使用path
const comp = simulate.render(id) // 渲染成自定义组件树实例
const parent = document.createElement('parent-wrapper') // 创建父亲节点
comp.attach(parent) // attach 到父亲节点上,此时会触发自定义组件的 attached 钩子
const view = comp.querySelector('.index') // 获取子组件 view
expect(view.dom.innerHTML).toBe('index.properties') // 测试渲染结果
expect(window.getComputedStyle(view.dom).color).toBe('green') // 测试渲染结果
})
b. 执行npm run test即可输出测试结果
进阶使用
1. 传入初始渲染 props
test('comp', () => {
// 前略
const comp = simulate.render(id, {
propName: 'propValue',
})
})
2. 获取数据
test('comp', () => {
// 前略
// 判断组件数据
expect(comp.data).toEqual({
a: 111,
})
})
3. 更新数据
test('comp', () => {
// 前略
// 更新组件数据
comp.setData({
a: 123,
})
})
4. 获取子组件
test('comp', () => {
// 前略
const childComp = comp.querySelector('#child-id')
expect(childComp.dom.innerHTML).toBe('<div>child</div>')
})
5. 获取子组件列表
test('comp', () => {
// 前略
const childrenComp = comp.querySelectorAll('.child-item')
expect(childrenComp.length).toBe(3)
})
6. 触发事件
test('comp', () => {
// 前略
comp.dispatchEvent('touchstart') // 触发组件的 touchstart 事件
childComp.dispatchEvent('tap') // 触发子组件的 tap 事件
})
7. 触发生命周期
test('comp', () => {
// 前略
comp.triggerLifeTime('ready') // 触发组件的 ready 生命周期
childComp.triggerLifeTime('moved') // 触发子组件的 moved 生命周期
})
8. 获取组件 this
test('comp', () => {
// 前略
const that = comp.instance // 注意,此处并不是返回 comp,comp 是在组件实例上再封装了一层的对象,而这里返回的是组件实例,即组件方法定义里的 this
that.data // 获取组件的 data 对象,这里和 comp.data 拿到的对象是一样的
that.xxx() // 调用组件 methods 定义段里定义的方法
})
覆盖率
编写单测后,最重要的衡量指标则是单测的覆盖率了,覆盖率指标包含:
1. 行覆盖率(line coverage):是否每一行都执行了?
2. 函数覆盖率(function coverage):是否每个函数都调用了?
3. 分支覆盖率(branch coverage):是否每个if代码块都执行了?
4. 语句覆盖率(statement coverage):是否每个语句都执行了?
覆盖率报告
在package.json中使用"jest --coverage"即可生成覆盖率测试报告,执行脚本后会在控制台输出对应文件的函数个数、行数等文件详情以及未覆盖的函数;同时会在项目根目录下生成coverage文件夹,在浏览器打开index.html即可查看友好的图示覆盖率详情。
踩坑日志
1. 小程序中不可避免的会使用大量的微信官方api,如wx.xxx,但是此测试环境是运行在 nodejs 上,不存在这些接口的底层实现。因此开发者在写测试用例时对于自己将要使用的接口进行模拟。
简单的例子:如下,若小程序ready生命周期中使用了 wx.getSystemSetting ,则需要在单测中对getSystemSetting进行期望结果的输出模拟。
wx.getSystemSetting = function () {
return {};
};
comp.triggerLifeTime('ready'); // 触发组件的 ready 生命周期
2. 小程序官方api非挂载在wx上的,如getCurrentPages等,需要挂载在global上,与开发者沟通后上述第一点处理方法类似:
issuse地址:微信官方api(非wx.xxx)无法成功调用
global.getCurrentPages = function (){
return [];
}
comp.triggerLifeTime('ready'); // 触发组件的 ready 生命周期
3. 未完待续…