文章目录
- 1. 目前流行的并行运行方法
-
- 1.1 官方 parallel 参数
- 1.2 个人开发者写的插件
- 1.3 基于 CI 平台的
- 2. GitLab CI Parallel
-
- 2.1 介绍
- 2.2 使用方法
- 3. 根据 CI_NODE_INDEX 生成测试目录
-
- 3.1 切分用例的不同方向
- 3.2 实现效果
- 3.3 源码
- 4. gitlab-ci.yaml 中的改动
-
- 4.1 由js脚本来生成测试目录
- 5. 效果如何?
-
- 5.1 耗时变小了
- 5.2 retry 成本变低了
随着用例越来越多,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: testparallel: 5script:- 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 = falsefiles.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: testparallel: 5script:- yarn cypress run --spec $(node scripts/cypress-parallel.js)-
如果你也需要传入业务线参数,那么是这样的
# gitlab-ci.yaml
# BUSINESS_LINE 可能是 C, 可能是 B
test:stage: testparallel: 5script:- yarn cypress run --spec $(node scripts/cypress-parallel.js -b ${BUSINESS_LINE})
如果你和我一样,通过一个参数来控制测试环境,那么效果是这样的。
# gitlab-ci.yaml
#TEST_ENV 可能是 dev, 可能是 prod
test:stage: testparallel: 5script:- 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 成本变低了,重跑只跑局部用例。