Github 操作上的 Mocha 并行作业
Mocha Parallel Jobs on Github Actions
我刚切换到 Mocha 中的并行测试,效果很好,但是,当我 运行 在 github 上使用它时,它似乎没有 运行ning在平行下。我需要配置什么才能让 Mocha 在 Github 操作上并行 运行?
.mocharc.json // 摩卡 9.x
{
"timeout": 5000,
"recursive": true,
"ui": "mocha-cakes-2",
"parallel": true,
"jobs": 4,
"checkLeaks": true,
"globals": [
"browser",
"mocha-cakes-2",
"regeneratorRuntime"
],
"exit": true,
"require": ["./test/helpers/setup.js"]
}
node.js.yml
name: Node.js CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present
- run: npm test
我通过以下方式让它工作:
- 通过
findAllTests.js
抓取所有测试文件
- 然后我用
jq
将这个文件的输出分成 3 个相等的块。
- 然后我把
npm run build
的神器存起来,准备在下一阶段使用。
- 然后我连续启动 3 个测试运行器 (随着项目的增长很容易配置)
- 最后,无论前面的任何步骤是否失败,我都会删除工件(我们不需要它们)
findAllTests.js
"use strict";
const fs = require("fs");
const path = require("path");
const files = [];
function shuffleArray(unshuffledArray) {
// Schwartzian transform. Will eventually help in evening out the test-time for each chunk.
return unshuffledArray
.map((a) => ({ sort: Math.random(), value: a }))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value);
}
/**
* Traverses through all subdirectories of a given folder.
* Used for Github Actions in order to aggregate and chunk all tests into even sizes.
*/
function findAllTests(directory) {
fs.readdirSync(directory).forEach((file) => {
const absolute = path.join(directory, file);
if (fs.statSync(absolute).isDirectory()) {
return findAllTests(absolute);
}
if (absolute.endsWith(".js") && !/(findAllTests|\/data\/|\/helpers\/)/.test(absolute)) {
return files.push(absolute);
}
});
}
function isMocha(context) {
return ["afterEach", "after", "beforeEach", "before", "describe", "it"].every((functionName) => {
return context[functionName] instanceof Function;
});
}
if (!isMocha(global)) {
findAllTests("./test/");
console.log(JSON.stringify(shuffleArray(files), null, 2)); // eslint-disable-line
}
node.yml.js
name: Node.js CI
on: [push]
jobs:
setup:
runs-on: ubuntu-latest
name: Setup
strategy:
matrix:
node-version: [14.x]
outputs:
test-chunks: ${{ steps['set-test-chunks'].outputs['test-chunks'] }}
test-chunk-ids: ${{ steps['set-test-chunk-ids'].outputs['test-chunk-ids'] }}
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
- name: Install & Build
run: |
npm ci
npm run build --if-present
- name: ESLint
run: npm run lint:js
- name: Save build artifacts
uses: actions/upload-artifact@v2
with:
name: public
path: public
- id: set-test-chunks
name: Set Chunks
run: echo "::set-output name=test-chunks::$(node ./test/findAllTests.js | jq -cM '[_nwise(length / 3 | ceil)]')"
- id: set-test-chunk-ids
name: Set Chunk IDs
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'] }}
mocha:
runs-on: ubuntu-latest
name: Test (chunk ${{ matrix.chunk }})
needs: setup
strategy:
matrix:
node-version: [14.x]
chunk: ${{ fromJson(needs.setup.outputs['test-chunk-ids']) }}
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- uses: actions/checkout@v2
- run: npm ci
- name: Download build artifacts
uses: actions/download-artifact@v2
- name: Mocha
run: echo $CHUNKS | jq '.[${{ matrix.chunk }}] | .[] | @text' | xargs npm run mocha:ga
env:
CHUNKS: ${{ needs.setup.outputs['test-chunks'] }}
cleanup:
runs-on: ubuntu-latest
name: Cleanup
needs: [setup, mocha]
if: always()
steps:
- uses: geekyeggo/delete-artifact@v1
with:
name: public
failOnError: false
mocharc.json
{
"timeout": 5000,
"recursive": true,
"ui": "mocha-cakes-2",
"checkLeaks": true,
"globals": [
"browser",
"document",
"mocha-cakes-2",
"regeneratorRuntime",
"WebSocket",
"window"
],
"exit": true,
"no-warnings": true,
"require": ["./test/helpers/setup.js"]
}
我刚切换到 Mocha 中的并行测试,效果很好,但是,当我 运行 在 github 上使用它时,它似乎没有 运行ning在平行下。我需要配置什么才能让 Mocha 在 Github 操作上并行 运行?
.mocharc.json // 摩卡 9.x
{
"timeout": 5000,
"recursive": true,
"ui": "mocha-cakes-2",
"parallel": true,
"jobs": 4,
"checkLeaks": true,
"globals": [
"browser",
"mocha-cakes-2",
"regeneratorRuntime"
],
"exit": true,
"require": ["./test/helpers/setup.js"]
}
node.js.yml
name: Node.js CI
on: [push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present
- run: npm test
我通过以下方式让它工作:
- 通过
findAllTests.js
抓取所有测试文件
- 然后我用
jq
将这个文件的输出分成 3 个相等的块。 - 然后我把
npm run build
的神器存起来,准备在下一阶段使用。 - 然后我连续启动 3 个测试运行器 (随着项目的增长很容易配置)
- 最后,无论前面的任何步骤是否失败,我都会删除工件(我们不需要它们)
findAllTests.js
"use strict";
const fs = require("fs");
const path = require("path");
const files = [];
function shuffleArray(unshuffledArray) {
// Schwartzian transform. Will eventually help in evening out the test-time for each chunk.
return unshuffledArray
.map((a) => ({ sort: Math.random(), value: a }))
.sort((a, b) => a.sort - b.sort)
.map((a) => a.value);
}
/**
* Traverses through all subdirectories of a given folder.
* Used for Github Actions in order to aggregate and chunk all tests into even sizes.
*/
function findAllTests(directory) {
fs.readdirSync(directory).forEach((file) => {
const absolute = path.join(directory, file);
if (fs.statSync(absolute).isDirectory()) {
return findAllTests(absolute);
}
if (absolute.endsWith(".js") && !/(findAllTests|\/data\/|\/helpers\/)/.test(absolute)) {
return files.push(absolute);
}
});
}
function isMocha(context) {
return ["afterEach", "after", "beforeEach", "before", "describe", "it"].every((functionName) => {
return context[functionName] instanceof Function;
});
}
if (!isMocha(global)) {
findAllTests("./test/");
console.log(JSON.stringify(shuffleArray(files), null, 2)); // eslint-disable-line
}
node.yml.js
name: Node.js CI
on: [push]
jobs:
setup:
runs-on: ubuntu-latest
name: Setup
strategy:
matrix:
node-version: [14.x]
outputs:
test-chunks: ${{ steps['set-test-chunks'].outputs['test-chunks'] }}
test-chunk-ids: ${{ steps['set-test-chunk-ids'].outputs['test-chunk-ids'] }}
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
- name: Install & Build
run: |
npm ci
npm run build --if-present
- name: ESLint
run: npm run lint:js
- name: Save build artifacts
uses: actions/upload-artifact@v2
with:
name: public
path: public
- id: set-test-chunks
name: Set Chunks
run: echo "::set-output name=test-chunks::$(node ./test/findAllTests.js | jq -cM '[_nwise(length / 3 | ceil)]')"
- id: set-test-chunk-ids
name: Set Chunk IDs
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'] }}
mocha:
runs-on: ubuntu-latest
name: Test (chunk ${{ matrix.chunk }})
needs: setup
strategy:
matrix:
node-version: [14.x]
chunk: ${{ fromJson(needs.setup.outputs['test-chunk-ids']) }}
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- uses: actions/checkout@v2
- run: npm ci
- name: Download build artifacts
uses: actions/download-artifact@v2
- name: Mocha
run: echo $CHUNKS | jq '.[${{ matrix.chunk }}] | .[] | @text' | xargs npm run mocha:ga
env:
CHUNKS: ${{ needs.setup.outputs['test-chunks'] }}
cleanup:
runs-on: ubuntu-latest
name: Cleanup
needs: [setup, mocha]
if: always()
steps:
- uses: geekyeggo/delete-artifact@v1
with:
name: public
failOnError: false
mocharc.json
{
"timeout": 5000,
"recursive": true,
"ui": "mocha-cakes-2",
"checkLeaks": true,
"globals": [
"browser",
"document",
"mocha-cakes-2",
"regeneratorRuntime",
"WebSocket",
"window"
],
"exit": true,
"no-warnings": true,
"require": ["./test/helpers/setup.js"]
}