Table of contents
1.
Introduction
2.
Parallel Tests with Mocha
3.
--parallel Flag 
4.
Features not compatible with parallel mode
4.1.
Unsupported reporters
4.2.
Exclusive Tests are not Allowed
4.3.
File Order is Non-Deterministic
4.4.
Test Duration Variability 
4.5.
Bail is Best Effort
4.6.
Root Hooks are not Global
4.7.
No Browser Support
5.
Root Hook Plugins
6.
Troubleshooting parallel mode
7.
FAQs
8.
Key Takeaways
Last Updated: Mar 27, 2024
Easy

Parallel Tests

Author Toohina Barua
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Mocha is a feature-rich JavaScript test framework that runs in the browser and on Node.js, making asynchronous testing simple and enjoyable. Mocha tests are run in sequential order, allowing for flexible and precise reporting while mapping uncaught exceptions to the appropriate test cases.
In this article, we'll look at how to perform parallel tests in Mocha with examples. Let us jump right into it!

Parallel Tests with Mocha

Mocha only ran tests in serial before v8.0.0: one test had to finish before moving on to the next. While this strategy has some advantages, such as being deterministic and quick on smaller test suites, it can become a bottleneck when many tests are run.
Mocha now supports running in the parallel mode under Node.js with version 8.0.0. Mocha can take advantage of multi-core CPUs by running tests in parallel mode, which results in significant speedups for large test suites.

--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:

  • --file
  • --sort 
  • --delay

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

  1. 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
  2. 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.
  3. 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
  4. 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.
  5. 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!

Live masterclass