没有 Cypress 仪表板的 Cypress 并行化
Posted
技术标签:
【中文标题】没有 Cypress 仪表板的 Cypress 并行化【英文标题】:Cypress Parallelisation Without Cypress Dashboard 【发布时间】:2020-09-10 09:17:01 【问题描述】:有没有一种方法可以在不访问 cypress 仪表板的情况下运行并行 cypress 执行程序?
我正在尝试让 cypress 并行运行我的测试,但您似乎必须有互联网连接才能访问 cypress 仪表板来记录测试。
任何人都知道我如何解决这个问题并在不依赖 Cypress Dashboard 或特定 CI 工具(如 Circle CI)的情况下并行运行测试?
提前致谢!
【问题讨论】:
【参考方案1】:我创建了一个Github Action,灵感来自Bielik 的答案,除其他功能外,它还允许指定测试运行器的数量。
执行由两部分组成: 首先,准备/安装步骤将测试拆分为规范(最好结合Cypress Dashbaord 的需求):
...
- uses: tgamauf/cypress-parallel@v1
这提供了两个输出变量integration-tests
,如果配置了component-tests
,则可以在测试步骤中使用:
strategy:
fail-fast: false
matrix:
spec: $ fromJson(needs.install.outputs.integration-tests)
...
- name: Execute tests
uses: cypress-io/github-action@v2
with:
# We have already installed all dependencies above
install: false
# Use the spec provided to this worker to execute the test
spec: $ matrix.spec
【讨论】:
【参考方案2】:所有这些答案看起来都很好,但我想要一些非常简单的东西,可以与 GitLab 一起使用,不需要额外的外部服务。我想出了一个非常简单的 Cypress 包装器:
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const glob = require('glob-promise');
async function getIntegrationDirectory()
let integrationFolder = 'cypress/integration';
let cypressJson = path.join(process.cwd(), 'cypress.json');
try
await fs.promises.access(cypressJson, fs.constants.F_OK);
let cypressConfig = require(cypressJson);
integrationFolder = cypressConfig.integrationFolder ?? integrationFolder;
catch (err)
// Ignore if file does not exist
return integrationFolder;
async function main()
let nodeIndex = parseInt(process.env.CI_NODE_INDEX ?? 1, 10);
let totalNodes = parseInt(process.env.CI_NODE_TOTAL ?? 1, 10);
let integrationFolder = await getIntegrationDirectory();
let specs = await glob(integrationFolder + '/**/*.js');
let start = (nodeIndex - 1) * specs.length / totalNodes | 0;
let end = nodeIndex * specs.length / totalNodes | 0;
let specsToRun = specs.slice(start, end);
let prefix = `Parallel Cypress: Worker $nodeIndex of $totalNodes`;
if (!specsToRun.length)
console.log(`$prefix, no specs to run, $specs.length total`);
return;
console.log(`$prefix, running specs $start + 1 to $end of $specs.length total`);
let args = process.argv.slice(2)
.concat(['--spec', specsToRun.join(',')]);
let child = childProcess.spawn('cypress', args, stdio: 'inherit', shell: true);
await new Promise((resolve, reject) =>
child.on('exit', exitCode =>
if (exitCode)
reject(new Error(`Child process exited with code $exitCode`));
resolve();
);
);
main().catch(err =>
console.error(err);
process.exit(1);
);
它需要安装glob-promise
和glob
。
它与 GitLab 的 parallel keyword 完美集成,只需极少修改即可与其他 CI 运行器一起使用。
要使用它,只需将 parallel: 4
添加到 GitLab CI 作业,然后调用 node parallelCypress.js <some options>
。然后它将扫描测试并将它们拆分为可用的并行作业,然后使用 --spec
选项调用 cypress,以便每个作业只运行一部分测试。
还有很多的改进空间,特别是因为并非所有规范文件都包含相同数量的测试,并且并非所有测试都具有相似的运行时。
【讨论】:
【参考方案3】:使用 GitHub Actions 我设计了一个可能有点矫枉过正的解决方案,但不需要 Cypress Dashboard 并且仍然允许用户并行运行他们的 e2e 测试。解决方案在私人项目中,所以我不能分享链接,但我可以给你简化的代码 sn-ps,希望足以让你开始。
我们的想法是使用 GitHub Actions 在单独的机器上运行每个规范。您在安装作业的某处运行此脚本(记得全局输出):
import * as core from '@actions/core';
import * as glob from 'glob';
export function setE2E()
const specs = getAllSpecFiles();
core.info(JSON.stringify( specs , null, 2));
core.setOutput('e2e-files', specs);
function getAllSpecFiles()
let projectRoot; // fill
let pattern; // fill eg. "./src/integration/**/*.spec.ts"
return glob.sync(pattern,
cwd: projectRoot,
dot: true,
silent: true,
follow: true,
nodir: true,
);
这将从您的 cypress 项目中获取所有 spec
文件。
然后在您的 e2e 工作中:
strategy:
fail-fast: false
matrix:
spec: $ fromJson(needs.install.outputs.e2e-files)
...
- name: Run Spec
run: npx cypress run --spec $ matrix.spec
这将为您的 cypress 项目中的每个规范生成单独的机器。正如我所说,这有点矫枉过正,但配置和完成工作非常简单。
【讨论】:
【参考方案4】:如果您使用 Github Actions,我创建了这个并行运行测试的工作流,每次并行运行最多 15 个规范文件,因此在添加新测试时它会自动扩展。它可能对您或其他人有所帮助。这也使应用程序在 https 模式下运行,但如果不需要,您可以删除这些行。
https://pastebin.com/ubx8BdUn
# This is a basic workflow to help you get started with Actions
name: Project
# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the master branch
on:
push:
branches: [master, preprod, staging]
pull_request:
branches: [master, preprod, staging]
jobs:
setup:
runs-on: ubuntu-latest
outputs:
# will contain a json string with an array of n elements, each being a string of spec files delimited by ,
test-chunks: $ steps['set-test-chunks'].outputs['test-chunks']
# json string with ids to use in the next job matrix depending on how many elements are in the above array, eg: [0,1]
test-chunk-ids: $ steps['set-test-chunk-ids'].outputs['test-chunk-ids']
steps:
- uses: actions/checkout@v2
- id: set-test-chunks
name: Set Chunks
# get all spec files from the integration directory, group them to be at most 15 at a time and transform them to json
run: echo "::set-output name=test-chunks::$(find cypress/integration -type f -name "*.spec.js" | xargs -n15 | tr ' ' ',' | jq -R . | jq -s -cM .)"
- id: set-test-chunk-ids
name: Set Chunk IDs
# get the number of elements from the above array as an array of indexes
run: echo "::set-output name=test-chunk-ids::$(echo $CHUNKS | jq -cM 'to_entries | map(.key)')"
env:
CHUNKS: $ steps['set-test-chunks'].outputs['test-chunks']
tests:
needs:
- setup
runs-on: ubuntu-latest
container:
# use cypress image, since just using node 12 doesn't work currently for some reason, gives node-sass error
image: cypress/browsers:node12.13.0-chrome78-ff70
options: "--ipc=host" # fix for a cypress bug
name: test (chunk $ matrix.chunk )
strategy:
matrix:
# will be for eg chunk: [0,1]
chunk: $ fromJson(needs.setup.outputs['test-chunk-ids'])
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Add domain to hosts file
run: echo "127.0.0.1 your.domain" | tee -a /etc/hosts
# cache cypress and node_modules for faster operation
- uses: actions/cache@v2
with:
path: '~/.cache/Cypress'
key: $ runner.os -cypress-$ hashFiles('**/yarn.lock')
- uses: actions/cache@v2
with:
path: '**/node_modules'
key: $ runner.os -modules-docker-$ hashFiles('**/yarn.lock')
# in case cache is not valid, install the dependencies
- run: yarn --frozen-lockfile
- run: yarn run cypress install
# run the frontend server in background and wait for it to be available
- run: PORT=443 HTTPS=true yarn ci-start &
- run: npx wait-on https://your.domain --timeout 180000
# the cypress docker doesn't contain jq, and we need it for easier parsing of json array string.
# This could be improved in the future, but only adds ~2s to the build time
- run: apt-get install jq -y
- name: Run Cypress
run: SPECS=$(echo $CHUNKS | jq -cMr '.[$ matrix.chunk ] | @text') && yarn cypress:ci --spec $SPECS
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
CHUNKS: $ needs.setup.outputs['test-chunks']
testsall:
if: $ always()
runs-on: ubuntu-latest
name: Tests All
needs: tests
steps:
- name: Check tests matrix status
if: $ needs.tests.result != 'success'
run: exit 1
【讨论】:
【参考方案5】:Testery.io 是一个基于云的测试平台,支持并行运行赛普拉斯测试。您可以注册免费计划以并行运行多达 5 个测试,将执行集成到您的 ci/cd 系统中,并在平台上查看结果。如果您选择付费计划,您还可以并行运行 15-30 次测试:https://testery.io/pricing。
【讨论】:
【参考方案6】:我创建了orchestrator 工具来执行此操作。它允许您部署任意数量的 cypress docker 容器,并在它们之间拆分所有规范,最后,它会生成一个漂亮的 html 报告。
它是开源的,免费使用,您可以将它与 Jenkins、TravisCI、github 操作或任何其他 CI 一起使用。
【讨论】:
【参考方案7】:如果使用 GitLab,您始终可以通过 .gitlab-ci.yml 文件手动拆分作业,如下所示:
1-job:
stage: acceptance-test
script:
- npm install
- npm i -g wait-on
- wait-on -t 60000 -i 5000 http://yourbuild
- npm run cypress -- --config baseUrl=http://yourbuild --spec ./**/yourspec1
2-job:
stage: acceptance-test
script:
- npm install
- npm i -g wait-on
- wait-on -t 60000 -i 5000 http://yourbuild
- npm run cypress -- --config baseUrl=http://yourbuild --spec ./**/yourspec2
【讨论】:
【参考方案8】:看看这个免费的解决方案https://github.com/agoldis/sorry-cypress 我只使用 docker-compose 中的 director 服务来让测试并行运行,举个简单的例子:
version: '3.7'
networks:
default:
external:
name: bridge
services:
cypress:
container_name: cypress
build:
context: ../
dockerfile: ./docker/cy.Dockerfile
links:
- director
ports:
- '5555:5555'
network_mode: bridge
cypress2:
container_name: cypress2
build:
context: ../
dockerfile: ./docker/cy.Dockerfile
links:
- director
ports:
- '5556:5556'
network_mode: bridge
mongo:
image: mongo:4.0
network_mode: bridge
ports:
- 27017:27017
director:
image: agoldis/sorry-cypress-director:latest
environment:
MONGODB_URI: "mongodb://mongo:27017"
network_mode: bridge
ports:
- 1234:1234
depends_on:
- mongo
【讨论】:
如果您使用stateless
或内存选项,我相信您甚至可能不需要mongodb
? github.com/agoldis/sorry-cypress#drivers
@floydrose 可以解释您为什么使用两个 cypress 服务?【参考方案9】:
尝试在 Buddy CI/CD 中添加 Cypress 操作。这样,您将在每次推送时构建和测试您的应用程序(并将它们部署到您想要的任何地方)。我认为 Buddy 应该帮助你解决这个问题。
enter image description here
【讨论】:
【参考方案10】:你可以试试这个cypress-parallel-specs-locally
【讨论】:
这将运行并行进程,但测试将按顺序执行。以上是关于没有 Cypress 仪表板的 Cypress 并行化的主要内容,如果未能解决你的问题,请参考以下文章
在 Browserstack 中运行后发布到 Cypress Dashboard
使用 cypress-cucumber-preprocessor 将现有的 cypress 测试转换为黄瓜风格的 bdd。第二个场景没有被选中