How Mocha is used to test itself

Craig Taub
3 min readDec 8, 2018

--

Mocha is a testing tool downloaded and used by millions of people every week. It is currently in its 5th version and has been around for over 7 years.

One of the aspects which draws people to Mocha is its ease of use and large suite of functionality. There are features for all sorts from using your own reporter to getting Desktop notifications, and it continues to grow.
So with all this functionality how do myself and the maintainers ensure future changes don’t let bugs creep in?
The obvious answer is it’s tested. But how do you test a tool whose job is to test? If you run a set of tests which you know should pass and they pass is that validation that Mocha is working?
The not-so-obvious answer is Mocha uses NodeJS child processes to achieve this.

So what is a child process?

In a nutshell it is a worker thread spawned for a specific purpose. Pipes for stdin/stdout and stderr are established (as well as listeners for certain events) between the parent process and the spawned child for communication.
There are several pros to using worker threads, these include:

  • process management -> delegating multiple tasks to be run and controlled at once
  • performance -> keep the master thread unblocked and isolate the heavier tasks
  • error resilience -> a child process could fail or terminate and the master can decide what to do with that information. It could log a message or restart a job.

So how does Mocha use child processes normally?

This is how you run Mocha normally (very simple)

mocha my-spec.js

Under-the-hood it spawns another binary (filename _mocha) inside a child, attaches listeners and runs the spec inside of that. So it’s actually more like

mocha -> spawn -> _mocha my-spec.js

The master binaries only job is to handle flags and spawning of the real mocha process.

Mocha leverages parts of child processes for different reasons, but that will not be the focus in this post.

How about using child processes when it comes to testing?

When it comes to integration testing Mocha makes use of “fixtures”. In this context a fixture is a normal looking test spec which has its output asserted by another test.

An example of a spec using a fixture looks something like this

+spec-calling-fixture.spec.jsit("should do X if ran with Y", function(done) {
runMocha("some-feature.fixture.js", function(output) {
expect(output).toBe(expectedResult)
done();
});
});

(note that certain features require Mochas context which does not work if you use arrow functions)

The “runMocha” function is executed which runs the below:

  1. calls the fixture file inside a new spawn of the root Mocha binary (with JSON reporter for easy processing of results)
  2. attaches listeners on the new child. For data on the stdout stream, data on the stderr stream and when the close event is fired.
  3. the streams are used to aggregate and store the output of the fixture
  4. On close event of the process the assertion callback is run with output as an argument.

Find the code located here.

So if the spec is run with:

mocha spec-calling-fixture.spec.js

Under-the-hood what is happening is:

mocha 
-> spawn -> _mocha spec-calling-fixture.spec.js
-> spawn -> mocha feature.fixture.js
-> spawn -> _mocha feature.fixture.js

This way we have turned a test specs output into a result which can be asserted against.

So there it is, the mechanism adopted by Mocha to allow it to test its suite of features. I should say this technique is only utilised when it is necessary.

I feel it is a very interesting use-case for child processes and how they work with events/streams and I hope you do too.

If you found this informative or at the very least mildly entertaining please spare a clap 🙏 😃

Craig

--

--