文章目录
随着用例越来越多,Cypress 脚本执行效率已经越来越慢了。我们尝试减少运行时间的过程中,做了一些事情。今天来分享一下。
1. 目前流行的并行运行方法
1.1 官方 parallel 参数
· 这种方法需要基于record参数,需要使用官方的云服务[链接]
1.2 个人开发者写的插件
· 这里不列举了,我尝试过了2~3个,都不太好用。
1.3 基于 CI 平台的
· 本文着重讲这个方法,参考了这位大佬的文章 [链接]
2. GitLab CI Parallel
2.1 介绍
GitLab CI 提供一种在Pipline中可以并行执行Job的方法
就是 parallel参数 [官方文档]
# gitlab-ci.yaml
test:
stage: test
parallel: 5
script:
- yarn cypress run --spec "./**/*.spec.js"
配置后,效果如图
2.2 使用方法
如果只是单纯的在 gitlab-ci.yaml 文件中加上 parallel: 5,只会让你的测试重复跑5遍,这显然不是我们想要的效果。
那么如何能5个并行任务执行不同的测试用例呢?
官方文档中指出,提供了2个参数,CI_NODE_INDEX 和 CI_NODE_TOTAL,分别是当前下标 与 总数量。
那么我们通过编写一个脚本,根据一定规则来切分用例为5份。然后运行cypress时,指定spec参数为不同的测试目录,是不是就可以实现了呢?
3. 根据 CI_NODE_INDEX 生成测试目录
3.1 切分用例的不同方向
前文提到的 大佬 的想法是,获取所有.spec.js文件,再均匀分配给所有并行job,分组对象是用例。
好处是执行时间更平均,但是业务结构被破坏了,个人更倾向于,按用例文件夹来分组,也就是分组对象是第一层文件夹(模块)。
大家可以参考我的脚本,也可以参考大佬的切分方法。其中我增加了 -b 参数来接收业务,再指定目录的逻辑,是基于业务,可以忽略。
3.2 实现效果
例如 /integration 目录下有10个文件夹,当前 CI_NODE_INDEX 是 1,CI_NODE_TOTAL 是 3的情况下,会返回 1,4,7,10 这4个目录。
3.3 源码
./scripts/cypress-parallel.js
const fs = require('fs')
const path = require('path')
const NODE_INDEX = Number(process.env.CI_NODE_INDEX || 1)
const NODE_TOTAL = Number(process.env.CI_NODE_TOTAL || 1)
var TEST_FOLDER = './cypress/integration'
var program = require('commander')
program.version('1.0.0').option('-b, --business_line [value]', '业务线', '').parse(process.argv)
/**
* 输出测试目录列表
*/
console.log(getSpecDirs().join(','))
/**
* 获取当前需要运行的测试目录
*/
function getSpecDirs () {
if (program.business_line.toUpperCase() === 'C') {
TEST_FOLDER += /c'
} else if (program.business_line.toUpperCase() === 'B') {
TEST_FOLDER += '/b'
}
const allSpecFiles = walk(TEST_FOLDER)
return allSpecFiles.sort().filter((_, index) => index % NODE_TOTAL === NODE_INDEX - 1)
}
/**
* 生成指定目录下所有可测试目录
* @param {string} dir 目录地址
*/
function walk (dir) {
const files = fs.readdirSync(dir)
var specDirs = []
var hasFile = false
files.forEach((file) => {
const filePath = path.join(dir, file)
const stats = fs.statSync(filePath)
if (stats.isDirectory() && ['pages'].indexOf(file)) {
specDirs.push(filePath + '/**/*.spec.js')
} else if (stats.isFile() && !hasFile && file.indexOf('spec.js') !== -1) {
specDirs.push(path.join(dir) + '/*.spec.js')
hasFile = true
}
})
return specDirs.reduce((all, folderContents) => all.concat(folderContents), [])
}
4. gitlab-ci.yaml 中的改动
4.1 由js脚本来生成测试目录
其实原理非常简单,用 $(node **.js) 调用js脚本,生成一个测试目录集合给spec参数。
# gitlab-ci.yaml
test:
stage: test
parallel: 5
script:
- yarn cypress run --spec $(node scripts/cypress-parallel.js)
-
如果你也需要传入业务线参数,那么是这样的
# gitlab-ci.yaml
# BUSINESS_LINE 可能是 C, 可能是 B
test:
stage: test
parallel: 5
script:
- yarn cypress run --spec $(node scripts/cypress-parallel.js -b ${BUSINESS_LINE})
如果你和我一样,通过一个参数来控制测试环境,那么效果是这样的。
# gitlab-ci.yaml
#TEST_ENV 可能是 dev, 可能是 prod
test:
stage: test
parallel: 5
script:
- yarn cypress run --spec $(node scripts/cypress-parallel.js -b ${BUSINESS_LINE}) --env config=${TEST_ENV}
5. 效果如何?
5.1 耗时变小了
原来需要48分钟执行完的脚本,只需要20分钟了。
5.2 retry 成本变低了
retry 成本变低了,重跑只跑局部用例。