1. 程式人生 > 其它 >GitLab CI 並行執行 Cypress 測試指令碼

GitLab CI 並行執行 Cypress 測試指令碼

技術標籤:自動化測試Cypress測試經驗CypressUI 測試軟體測試

文章目錄

隨著用例越來越多,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 成本變低了,重跑只跑區域性用例。