Parallel
May 27, 2026 ยท View on GitHub
Cucumber supports running scenarios in parallel. The main process becomes a "coordinator" and spins up several worker threads to be the "workers". You can enable this with the parallel configuration option:
- In a configuration file
{ parallel: 3 } - On the CLI
cucumber-js --parallel 3
The number you provide is the number of workers that will run scenarios in parallel.
Each worker receives the following env variables (as well as a copy of process.env from the coordinator process):
CUCUMBER_PARALLEL- set to 'true'CUCUMBER_TOTAL_WORKERS- set to the number of workersCUCUMBER_WORKER_ID- ID for worker ('0', '1', '2', etc.)
Timing
When using parallel mode, the last line of the summary output differentiates between real time elapsed during the test run and aggregate time spent actually running steps:
73 scenarios (73 passed)
512 steps (512 passed)
0m51.627s (executing steps: 4m51.228s)
Worker options
Workers run as threads inside the main process, so they share its resources. You can tune them via the workerOptions configuration option, which is passed through to the Worker constructor:
{
"parallel": 4,
"workerOptions": {
"resourceLimits": {
"maxOldGenerationSizeMb": 2048
}
}
}
Hooks
When using parallel mode, any BeforeAll and AfterAll hooks you have defined will run once per worker.
Custom work assignment
If you would like to prevent specific sets of scenarios from running in parallel you can use setParallelCanAssign.
Example:
setParallelCanAssign(function (pickleInQuestion, picklesInProgress) {
// Only one pickle with the word example in the name can run at a time
if (pickleInQuestion.name.includes('example')) {
return picklesInProgress.every((p) => !p.name.includes('example'))
}
// No other restrictions
return true
})
For convenience, the following helpers exist to build a canAssignFn:
import {
setParallelCanAssign,
parallelCanAssignHelpers,
} from '@cucumber/cucumber'
const { atMostOnePicklePerTag } = parallelCanAssignHelpers
const myTagRule = atMostOnePicklePerTag(['@tag1', '@tag2'])
// Only one pickle with @tag1 can run at a time
// AND only one pickle with @tag2 can run at a time
setParallelCanAssign(myTagRule)
// If you want to join a tag rule with other rules you can compose them like so:
const myCustomRule = function (pickleInQuestion, picklesInProgress) {
// ...
}
setParallelCanAssign(function (pickleInQuestion, picklesInProgress) {
return (
myCustomRule(pickleInQuestion, picklesInProgress) &&
myTagRule(pickleInQuestion, picklesInProgress)
)
})
Formatting
You can access workerId property in testCaseStarted envelope object:
const { Formatter } = require('@cucumber/cucumber')
class ExampleFormatter extends Formatter {
constructor(options) {
options.eventBroadcaster.on('envelope', (envelope) => {
if (envelope.testCaseStarted) {
if (envelope.testCaseStarted.workerId) {
console.log(
`the event has been fired from a worker with id ${envelope.testCaseStarted.workerId}`
)
} else {
console.log('the event has been sent from the main thread')
}
}
})
super(options)
}
}
module.exports = ExampleFormatter