本文作者
Cypress是常见的UI测试框架之一,UI测试的场景不做赘述,本文旨在探索Cypress检测页面首屏加载时间的最佳实践,目的是为了检测页面首屏加载的性能。文章主要包含实现逻辑、数据保存和报告生成三部分。
API介绍
本文主要使用到了Cypress提供的hooks,包含before、after、beforeEach和afterEach,可理解为测试用例不同的生命周期,具体说明如下:
describe('Hooks', () => {
before(() => {
// runs once before all tests in the block
// 在所有用例之前触发一次
})
after(() => {
// runs once after all tests in the block
// 在所有用例之后触发一次
})
beforeEach(() => {
// runs before each test in the block
// 在每个用例之前触发一次
})
afterEach(() => {
// runs after each test in the block
// 在每个用例之后触发一次
})
})
实现逻辑
说到页面首屏加载时间,我们不难想到可以通过获取页面加载前后时间戳之差来计算,根据刚刚介绍的hooks函数,我们可利用before和after钩子实现简单逻辑,代码如下
let startTime;
const url = '';
describe('test fp', () => {
before(() => {
startTime = +new Date();
})
after(() => {
const fp = +new Date() - startTime;
})
it(() => {
cy.visit(`${url}`);
})
})
这段代码存在两个比较明显的问题
- 没有描述页面完成加载的标志,代码层面表现为cy.visit()没有给出断言,这样会导致计算出的首屏加载时间不准确。
- 只对页面的首屏加载时间进行一次计算,结果可能存在误差,需要完善为多次测试取均值。
针对以上两个问题,我们把用例进行调整,利用dom结构进行断言确认页面已完成加载;并增加一个for循环测试该用例,利用beforeEach和afterEach钩子完成首屏加载时间计算。代码如下,最终5次测试结果保存在fpArr数组中
const TEST_TIMES = 5;
let startTime;
let fpArr = [];
const url = '';
describe('test fp', () => {
beforeEach(() => {
startTime = +new Date();
})
afterEach(() => {
fpArr.push(+new Date() - startTime);
})
for (let i = 0; i < TEST_TIMES; i++) {
it((`加载页面`) => {
cy.visit(`${url}`)
.get('.app-container', { timeout: 10000 })
.should('have.length', 1);
});
}
})
数据保存
为了完善用例,可将历史数据进行保存记录,便于后续数据提取对比。实现思路如下
定义cy.collectData命令,将数据序列化后以参数的形式传入,执行node collectData.js,完成数据写入,如下
Cypress.Commands.add('collectData', data => {
// 将参数序列化
const argv = ...
cy.exec(`node collectData.js ${argv}`);
});
collectData.js主要是利用fs提供的文件读写能力完成数据写入,如下 。
// 将参数反序列化
const argv = ...
// 读取已有数据并合并数据
const filePath = path.join(process.cwd(), path.join('cypress/data', argv.dir));
const fileJSON = JSON.parse(fs.readFileSync(filePath).toString() || '[]');
fileJSON.push(argv);
// 写入
fs.writeFileSync(filePath, JSON.stringify(fileJSON, null, ' '));
接着便可通过cy.collectData收集首屏加载时间,具体用法如下。
cy.collectData({
dir: 'fp.json',
startTime,
fp,
});
报告生成
对于测试用例而言,报告的可读性和可用性及其重要,好的测试报告可以帮开发者快速获取结果、定位问题。
在本文的场景中,首屏性能不能仅以是或否通过这种简单标准来衡量,还需要在报告中提供一份首屏加载时间数据便于查看分析,因此需要将测试数据层现在报告中。
使用mochawesome模块可以生成漂亮的HTML、JSON等格式的报告,mochawesome提供的addContext API可以将附加信息记录在mochawesome的报告中,使用方法如下
const addContext = require('mochawesome/addContext');
cy.once('test:after:run', test => {
addContext({ test }, {
title: '首屏时长',
value: 234,
});
});
可以将该方法封装为一个Cypress Command(如下),通过cy.addContext使用。
const addContext = require('mochawesome/addContext');
Cypress.Commands.add('addContext', (context) => {
cy.once('test:after:run', test => {
addContext({ test }, context);
});
});
使用addContext记录的数据,最终会层现在生成的报告(以JSON报告为例)中。 如下,context字段记录着我们通过addContext增加的信息。
[mochawesome_0001.json]
然而addContext有一个局限性,即mochawesome没有将该字段数据层现在可视化报告中,因此可读性较差。笔者摸索了一种方式在可视化报告中打印这部分信息。
基于mochawesome模块的源码,在mochawesome/src/mochawesome.js中,增加consoleContext逻辑。以4.1.0版本为例,在runner的end事件回调中增加consoleContext(suites)
,如下
function Mochawesome(runner, options) {
...
runner.on('end', () => {
try {
const suites = this.runner.suite.suites;
consoleContext(suites);
...
} catch (e) {
// required because thrown errors are not handled directly in the
// event emitter pattern and mocha does not have an "on error"
/* istanbul ignore next */
log(`Problem with mochawesome: ${e.stack}`, 'error');
}
});
}
consoleContext的思路是提取每个用例结果中的context,并将其组合在一个对象中,利用console.table打印出来。代码如下,
function consoleContext(suites) {
var family = {};
for (let i = 0; i < suites.length; i++) {
const tests = suites[i].tests;
for (let j = 0; j < tests.length; j++) {
const t = tests[j];
if (!t.context) {
continue;
}
Object.keys(t.context.fields).forEach(key => {
const value = t.context.fields[key];
!family[t.title] && (family[t.title] = {});
family[t.title][key] = value;
});
}
}
console.table(family);
}
最终使用方法为
cy.addContext({
title: '首屏时长',
value: '首屏时长',
fields: {
'页面url': 'http://baidu.com',
'版本': '0.0.1',
'第一次加载/ms': 2000,
}
});
如下图的表格,成功将context数据可视化。 当然,你也可以参考mochawesome自定义一个reporter来完成报告输出。
小结
回到一开始的首屏时间测试用例,结合以上介绍的逻辑实现、数据保存和报告生成,最终我们可以写出一个完整的用例
const TEST_TIMES = 5;
let startTime;
let fpArr = [];
const url = '';
describe('test fp', () => {
beforeEach(() => {
startTime = +new Date();
})
afterEach(() => {
const fp = +new Date() - startTime;
fpArr.push(fp);
// 记录数据写入本地文件
cy.collectData({
dir: 'fp.json',
pageUrl: url,
fp,
version: '0.0.1',
});
// 打印context信息
let fields = {};
fpArr.forEach((item, index) => {
fields[`第${index+1}次/ms`] = item;
});
cy.addContext({
fields,
title: '首屏时长',
value: '首屏时长'
});
})
for (let i = 0; i < TEST_TIMES; i++) {
it((`加载页面`) => {
cy.visit(`${url}`)
.get('.app-container', { timeout: 10000 })
.should('have.length', 1);
});
}
})
生成报告如下
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!