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