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.