Set Up Ava Test Runner with FeathersJS and Mongoose

Getting started with testing is difficult. When you have slowly worked up enough experience with testing that you want to apply it to the front-end, another language, or a blank project, it can be just as daunting as when you first began.

I had a lot of trouble trying to get Ava set up with FeathersJS and MongoDB and thought it would be useful to share my findings and eventual solution.

I will be working with a stock install of the FeathersJS framework, so please refer to their documentation if you want to follow along and don't have one set up already.

Ava

🚀 Futuristic JavaScript test runner

Ava is a modern test runner for JavaScript applications and wraps your test cases in asynchronous callbacks so that they can effectively be run in parallel. It makes use of a single processing thread, but because your tests are now non-blocking, they don't have to wait on results before others can be executed.

Installing Ava

In the main folder of your Feathers app, you should run either yarn add ava --dev or npm install ava --save-dev.

Now when you run yarn run test or npm test Ava will try to find tests in the test directory, and will run any files that end with *.test.js

Asynchronous Woes

When you are running any code asynchronously, you are in danger of experiencing what is known as a race condition.

A race condition or race hazard is the behavior of an electronic, software, or other system where the output is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when events do not happen in the order the programmer intended. - Wikipedia

What can happen (and indeed what led me to this blog post) is that you can modify some state like a database record, and before your asynchronous test case has completed, a different test case has already modified the database in some way. This leads to test cases that occasionally work but often break with no clear reason.

In the example above, the single database in use by the ODM is accessed by both test cases. The order that team.save() resolves in are not deterministic and so you can end up with the second test completing before the first.

You can survive by making your tests only applicable to the models you're dealing with.
For example, if I used Team.findById(team._id); in the first case, I could confirm that the team was indeed created.

Unfortunately this peace doesn't last forever, and you can't rely on the database being in any particular state for each test. You even run into unique constraint issues from previous tests because you couldn't know when to safely clear and reset your database!

The Good Part

You are now in a position to run asynchronous tests against MongoDB!
Here is refactored version of our first test example which now uses FeathersJS services with a mock application and a test mongoose instance:

Final Words

It has taken me several days (I'm new to the Node world) and a lot of hard work but I am happy with the implementation. I can now test as I want with the added benefits that Ava brings in terms of speed.

Some of the example test cases above are written free-hand, so may not work exactly as prescribed. I trust you know how to follow a stack trace. With that said, all of the code mocking the FeathersJS app and Mongoose is real.

If you have found this useful or have any questions or comments, feel free to post below.