Over a period of time, your application will have more lines of code for testing than the actual application logic. So I feel it’s a good investment to learn to write tests and follow a Test Driven Development or TDD approach.

There are 4 main types of tests namely: End to End, Integration, Unit and Static.

Static testing helps to find out typos and basic syntax errors. Unit testing is what we are going to learn in this post where we test one single unit at a time with isolation from other functionalities.

Tip: Bit (open source) is a great power tool that helps you isolate, test and use JS components. You can use it to easily share and test JS components, making them reusable out-of-the-box for new projects. Feel free to give it a try :)

3. Integration testing is where you will find out whether each of your units of functionalities works with each other.

4. End-to-End or e2e testing as the name suggests refers to testing the complete flow of the project from start to end.

In this post, I will setup a basic testing suite to make our Create Read Update and Delete (CRUD) operations on the MongoDB database with the Mongoose library more robust using the most popular Mocha testing framework.

Initial setup

Installing packages

Create a directory called pokemons and run npm init

mkdir pokemons

cd pokemons

npm init

npm init creates a package.json file. This file stores the information of all the packages and scripts that are required to run your application. Enter the details or just press enter multiple times to use the default setting.

Using npm (node’s package manager) we will install 3 packages.

npm install --save mocha nodemon mongoose

Mongoose is a library which helps to modify the database.

Mocha testing framework is popular for testing Nodejs and we are going to test create, read, update and delete operations on our MongoDB database.

Directory structure

Open the project folder with your preferred text editor.

Start MongoDB and connect to it.

I am assuming you have MongoDB already installed on your computer. We will start the daemon specifying the location of the database.

mongod --dbpath ~/mongo-data/

You can install Robo 3T which gives a good user interface to manage your MongoDB database.

//inside tests/test_helper.js const mongoose = require('mongoose'); //tell mongoose to use es6 implementation of promises

mongoose.Promise = global.Promise; mongoose.connect('mongodb://localhost/pokemons');

mongoose.connection

.once('open', () => console.log('Connected!'))

.on('error', (error) => {

console.warn('Error : ',error);

}); //Called hooks which runs before something.

beforeEach((done) => {

mongoose.connection.collections.pokemons.drop(() => {

//this function runs after the drop is completed

done(); //go ahead everything is done now.

});

});

This the basic connection helper that you are going to use in most of your Node.js-MongoDB project. After we install mongoose as a package to our project, you need to require it in your main file. This is because all packages in Node.js execute within their scope.

The hook beforeEach() helps to empty our database before we run our tests. This will become more clear ahead when we write an actual test for create operation.

Deleting users take time, so we need to hold mocha before the operation completes. The done() function call notifies that the operation is complete now you can resume the execution.

The above code is connecting to pokemons database on a locally installed version of MongoDB. If the database is not present, it will make one.

.once() and .on() are called event handlers. The first argument is the event and the second is the function to execute once the event occurs.

Mongoose and Mocha

Pokemon model will represent all the data in a single collection in our database. The properties, type and validation of data of the instance is defined by a schema.

A detailed post on the terminologies we use with NoSQL databases is covered by me in the following article.

//inside src/pokemon.js

const mongoose = require('mongoose');

const Schema = mongoose.Schema; const PokemonSchema = new Schema({

name: {

type: String,

required: [true, 'Name is required.']

},

type: String

}) //Pokemon constant represents the entire collection of data

const Pokemon = mongoose.model('Pokemon', PokemonSchema); module.exports = Pokemon;

We will start mocha and ask it to watch our project for changes. When we change something and save our file, the hooks will empty the database and run our test.

Testing flowchart

//inside package.json

"scripts": {

"test": "nodemon --exec 'mocha -R min'"

}

The -R and min options are used to condense the output and clear the previous outputs. The following command will not do anything just yet, you can run it after you write your first test.

npm run test

CRUD

Mocha provides us a function called describe() which takes in 2 arguments: the first one is a string which will help you identify the group of test that’s failing or passing, the second is a function to implement the testing logic.

A describe() may contain more than one tests inside a it() function which are closely related to each other. So as in our case, we are making different files for each operation in CRUD. But, we could have merged them in a single file with different describe blocks.

Every test needs you to assert something, say assert(2+2 is 4). For this, we need to require the assert package in our file.

CREATE & READ

In all the tests, poke is the actual instance of the Pokemon model.

All the explanations are in the code as comments.

DELETE & UPDATE

In the delete_test.js file you may see a lot of repetitive code. I will show you how to refactor it in the update_test.js file.

Conclusion

So now you have a testing suit ready for your next project. Testing is less boring than finding the code which breaks your project. After reading this post, you should now be confident enough to write unit tests for your code.

I publish on other platforms too! So stay connected.

A lot of people including me find testing intimidating in the beginning. So once you start writing basic tests like the ones mentioned in this post and understand the flow and working of testing, you will be ready to test bigger and complex projects within no time.

I would like to end with this awesome tweet by Sam Saccone.