--parallel Flag
In many cases, simply passing --parallel to the mocha executable is enough to enable parallel test mode. Consider the following scenario:
mocha --parallel test/*.spec.js
Features not compatible with parallel mode
Due to technical limitations and reasons, a few features aren't compatible with parallel mode. They are:
Unsupported reporters
The following reporters are unable to work when running tests in parallel due to their nature:
- progress
- json-stream
- markdown
These journalists expect Mocha to reveal how many tests it intends to run before execution. In parallel mode, this information is unavailable because test files are only loaded when they are about to be run.
The results of the tests will "stream" as they happen in serial mode. Reporter output is buffered in parallel mode; reporting occurs after each file is completed. The reporter output will be presented in "chunks" in practice (but will otherwise be identical). There may be a significant pause while a test file is running if it is particularly slow.
Exclusive Tests are not Allowed
You are not permitted to use it. In parallel mode, use only, describe.only, this.only(), and so on. This is because, like the incompatible reporters mentioned earlier, Mocha does not load all files and suites into memory before running tests in parallel mode.
Workarounds that have been suggested include:
- Instead, use --grep or --fgrep; it's not as efficient, but it'll suffice.
- Parallel test mode should not be used. You're unlikely to run many exclusive tests, so parallel mode won't help you much.
File Order is Non-Deterministic
Mocha neither guarantees the order in which test files will run in parallel mode nor which worker process will run them.
As a result, the following options that are order-dependent cannot be used in parallel mode:
Test Duration Variability
Running tests in parallel consumes more system resources by definition. The OS may take longer to schedule and complete some operations depending on system load. As a result, individual test timeouts may need to be increased globally or individually.
Bail is Best Effort
When used with --bail (or this.bail()) to exit after the first failure, other tests are likely to run concurrently. Before exiting, Mocha must shut down its worker processes.
Subprocesses can also throw uncaught exceptions. Mocha will "bubble" this exception to the main process when used with --allow-uncaught, but it will still have to shut down its processes.
Mocha will end the test run "very soon" in either case.
Root Hooks are not Global
A root hook is a hook that is not defined within a suite and is found in a test file. An example of how to use the bdd interface:
// test/setup.js
// root hook to run before every test (even in other files)
beforeEach(function () {
doMySetup();
});
// root hook to run after every test (even in other files)
afterEach(function () {
doMyTeardown();
});
When this command is used (in the default "serial" mode):
mocha --file "./test/setup.js" "./test/**/*.spec.js"
The setup.js script will be run first, and it will install the two hooks mentioned above for each test in./test/**/*.spec.js.
In parallel test mode, the preceding example does not work.
When Mocha runs in parallel mode, test files are not assigned to the same process or instance of Mocha. As a result, if a hypothetical root hook is defined in test file A, it will not appear in test file B.
Here are a few ideas for workarounds:
- At the top of each test file, use require('./setup.js') or import './setup.js'. Those who dislike boilerplate should avoid it.
- Recommended: Using the new (as of v8.0.0) Root Hook Plugin system, define root hooks in a "required" file.
Instead, use a global fixture if you only need to run some code once.
No Browser Support
For the time being, the parallel mode is only available in Node.js.
Root Hook Plugins
Root Hook Plugins are CJS or ESM modules with a named export, mochaHooks, where the user can define hooks at will. Mocha's --require option is used to load Root Hook Plugin modules.
A more detailed explanation and examples can be found in the documentation (linked above), but here's a simple example.
Assume you have a project that uses the --file hooks.js command to load root hooks:
// hooks.js
beforeEach(function() {
// do something before every test
this.timeout(5000);
});
To convert this to a Root Hook Plugin, change hooks.js to be:
// hooks.js
exports.mochaHooks = {
beforeEach() {
this.timeout(5000);
}
};
Replace —file hooks.js with —require hooks.js when calling the mocha executable.
Troubleshooting parallel mode
While parallel test mode should work for most projects, if you're having trouble, use this checklist to help you prepare your tests:
- Make sure you're using a reporter that is supported.
- Make sure you're not using any other flags that aren't supported.
- Double-check your config file; any command-line option will be merged with any config file options.
- In your tests, look for root hooks (they look like this). Make a root hook plugin with them.
- Do you use root hooks in any of the assertion, mock, or other test libraries you're using? For parallel test mode compatibility, they may need to be migrated.
- If your tests are timing out unexpectedly, you may need to increase the default test timeout (via —timeout)
- Make sure your tests don't require a specific order to run.
- Make sure your tests clean up after themselves by removing temp files, handles, and sockets, among other things. Attempting to share state or resources between test files is not a good idea.
FAQs
-
What can we do instead of using --parallel flag?
Alternatively, you can use a Mocha configuration file to specify any command-line flag. Mocha's default configuration is stored in a YAML file,. Mocharc.yml.
Add parallel: true to this file to enable parallel mode:
# .mocharc.yml w/ parallel mode enabled
require: 'test/setup'
ui: 'bdd'
timeout: 300
parallel: true
-
On using --parallel flag and running npm test, we get a bunch of timeout exceptions in the unit tests, which use the default timeout value. Why?
Parallel mode exhibits a much wider range of timings for any given test due to many variables. These variables range from Mocha to Node.js to the OS to the CPU itself. These timeout exceptions are a symptom of a naturally higher system load and nondeterministic execution order, not a newfound performance issue.
-
How do you resolve the above issue of getting a bunch of timeout exceptions in the unit tests?
To fix the above issue, we increase Mocha's default test timeout from 300 milliseconds (0.3 seconds) to 1000 milliseconds (1 second):
# .mocharc.yml
# ...
timeout: 1000
-
Why should we avoid using parallel mode when we have a single test file?
Individual tests are not run in parallel by Mocha. Mocha runs multiple test files at the same time. It costs to spawn worker processes. If you give Mocha a single, lonely test file, it will create a single worker process, which will run the file. You'll be penalized for using parallel mode if you only have one test file.
-
How can we disable parallel mode?
If parallel mode is defined in your config file, you can use the —no-parallel flag or reduce the job count, e.g., --jobs=0, to temporarily disable it on the command line.
Key Takeaways
In this article, we have extensively discussed parallel tests with Mocha.
We hope that this blog has helped you enhance your knowledge regarding parallel tests and if you would like to learn more, check out our articles on Coding Ninjas Studio. Do upvote our blog to help other ninjas grow. Happy Coding!