Node testing with Ava

Ava is a relatively new test runner (it deems itself as a “Futuristic test runner”). The number of node projects using mocha is staggering, but I wanted to try out Ava - it boasts some big performance boosts, requires atomic tests, and is actively in development.

One thing I’ve really already liked about Ava so far is the community. I ran into an issue the other night when setting up the test harness for my new project. Ava runs each test file in a different process, which is different than mocha - mocha runs all tests in the same process. Since I’m testing an API server, I started my server at the beginning of my tests by require-ing my server file in a before block. In mocha, this file is only loaded once due to node’s require mechanism caching files. However, because Ava runs each test file in a separate process, this caching never occurs in practice as each test file is an isolated environment. Since I use a specific port while testing, this caused an issue - my tests would throw an EADDRINUSE error since a server would attempt to listen on the same port for each test.

I reached out to the Ava gitter chat, as this had to be an issue that someone had already solved. I got answer almost immediately (shout out to @aretecode) and ended up with creating a script that would run my setup tasks in one process and run Ava in another. This probably could have been done via gulp or another task runner, but I used execa and ended up with the below.

const execa = require('execa');

const server = execa('node', ['dist/index.js'], ['cleanup']);

server.stdout.pipe(process.stdout);
server.stderr.pipe(process.stderr);

const test = execa('ava', ['-v'], ['cleanup']);

test.stdout.pipe(process.stdout);
test.stderr.pipe(process.stderr);

test
  .then(() => process.exit())
  .catch(() => process.exit(1));

This loads my server (in dist/index.js), then launches a separate test process - both are piped to the stdout and stderr of this process. execa provides a nice Promise-like interface, so we exit the process with a non-zero exit code if there’s an error, or just successfully close the process if everything ended up alright. I ended up putting this in a separate runner.js file within my test directory, and set my npm test script to be NODE_ENV=test node test/runner.js. The server loads once, and each test process runs successfully against that server.