Hello and welcome to the fifth and final part of my tutorial series Building a REST API with MongoDB, Node and Express. As I mentioned in part 4 manual testing was getting tedious. In this lesson we will fix that through using Gulp and Mocha. If you need a refresher on these tools check out the beginning of the series.

As always the code is available from this repository: http://gogs.dev.dylanscott.me/dylanrhysscott/series-rest-api

Install our dependencies

Before we can begin we need to install a few deps to our project. We are going to install nodemon (a daemon which will restart our app), gulp and several plugins which we can use for testing. Run the following command to save them for development:

npm install nodemon gulp gulp-nodemon gulp-mocha --save-dev  

Lastly open your package JSON and change the start script to the gulp command. This will make sense in a minute but for now make sure it looks like this:

 "scripts": {
    "start": "gulp"
  },

Creating our gulp file and test suites

Currently our app will not work if we run npm start. Why? We haven't written a gulpfile yet. A gulpfile is what defines our tasks for gulp to run. We need to create two tasks one to start the app via nodemon and one to run our tests:

  1. First create a gulpfile.js in the root of the project.
  2. Require our plugins like so:
const gulp = require('gulp');  
const mocha = require('gulp-mocha');  
const nodemon = require('gulp-nodemon');  
  1. Define a default task that runs our ./bin/www script with Nodemon. You'll also note we've passed a few options to Nodemon which tells it what files it should restart on change and what tasks to do prior to a restart.
gulp.task('default', () => {  
    nodemon({
        script: './bin/www',
        ext: 'js',
        tasks: ['mocha'],
        env: { 'NODE_ENV': 'development' }
    })
});
  1. We now should write our missing mocha test as defined in the task above. In this test we read our entrypoint for our tests using gulp.src() which is tests/index.js and pass it to Mocha to run.
gulp.task('mocha', () => {  
    gulp.src(process.cwd() + '/tests/index.js', {read: false})
    .pipe(
        mocha({
            reporter: 'nyan'
        })
    );
});
  1. That's great we have our tasks set up - Now we need to create our missing files. Create the tests/index.js file and populate it with these two statements:
const users = require('./user');  
const posts = require('./post'); // Fill this in!  

Also create our required files tests/user.js and tests/post.js. Now we're all setup you can run npm start and will see an output similar to this:

[00:58:34] [nodemon] starting `node ./bin/www`
[00:58:42] [nodemon] restarting due to changes...
[00:58:42] [nodemon] running tasks...
[00:58:42] Using gulpfile ~/Documents/projects/series-rest-api/gulpfile.js
[00:58:42] Starting 'mocha'...
[00:58:42] Finished 'mocha' after 8.08 ms

Writing the user test suite

So what is considered a test? Well a test accepts an input and checks an output against what is expected. If everything is correct it passes otherwise it fails. To help with checking our I/O I am going to use an Assertion Library called Chai.js. It provides many helpful functions for us so lets install that now:

npm install chai --save-dev  

Lets also install a request library to help call our API:

npm install request --save-dev  

How does Mocha work - Well it works by defining a suite using a describe() block. Within the suite block it has tests which are plain english strings explaining the test. As its async each test accepts a callback with a done() parameter which is called when the test finishes. This allows Mocha to move through the tests. Clear as mud?! I thought so lets stub out an empty test suite in tests/user.js:

const chai = require('chai');  
const request = require('request');  
const should = chai.should;  
const assert = chai.assert;  
const expect = chai.expect;  
const host = 'http://localhost:3000';

describe('User API Tests', () => {  
    var userId = "";

    it('Should return a full list of users', (done) => {

    });

    it('Should create a new user', (done) => {

    });

    it('Should get a single user', (done) => {

    });

    it('Should update a single user', (done) => {

});

As you can see I have:

  • Required our packages and assigned some functions to variables for easy access
  • Assigned our host URL to an easy variable and created a userID var in the suite so we can store data between requests
  • Created our suite with describe()
  • Created all my tests with callbacks and parameters in the it() block

Lets fill our each test!

  1. GET to /users - We pass in our host data and call some Chai helper functions assert.equal() and expect() to define our pass / fail conditions. As you can see it's all pretty plain english! When the test is finished we call done()
request.get(host + '/users', (req, res) => {  
            assert.equal(res.statusCode, 200);
            expect(res.body).to.not.be.null;
            done();
        });
  1. POST to /users - Pretty much the same as above but we create our data and pass it to a form object of our request. We also save the user ID from the request for use later
var user = {  
            firstName: 'Test',
            lastName: 'User',
            dob: '2016-12-01',
            email: 'example@example.com'
        };

        request.post(host + '/users', {form: user}, (req, res) => {
            assert.equal(res.statusCode, 200);
            expect(res.body).to.not.be.null;
            userId = JSON.parse(res.body)._id;
            done();
        });
  1. GET request to /users/:id - Same as the first get request but we are passing in our saved user ID.
request.get(host + '/users/' + userId, (req, res) => {  
            assert.equal(res.statusCode, 200);
            expect(res.body).to.not.be.null;
            done();
        });
  1. PUT request to /users/:id - Same as our post request however we are passing in our updated data and the saved user ID
var data = {  
            firstName: 'Updated Name'
        };

        request.put(host + '/users/' + userId, {form: data}, (req, res) => {
            assert.equal(res.statusCode, 200);
            expect(res.body).to.not.be.null;
            done();
        });
  1. DELETE request to /users/:id - Pretty much the same as a get request to a single user.
request.delete(host + '/users/' + userId, (req, res) => {  
            assert.equal(res.statusCode, 204);
            expect(res.body).to.be.empty;
            done();
        });

Testing it all out...

Run the app by running npm start and save a .js file. You should see an output similar to below. This will happen on each save giving you instant feedback on functionality of your API. Neat!

gulp-js-test-results

Wrapping up

Well thats it! If you've made it this far we're at the end of the series :(. I have left the tests/post.js file for you to use as practice. It's worth noting here we have only done basic testing such as presence checks and equality checks. There maybe many more extensive tests you wish to run - Check out the documentation for Chai for more useful functions as well as checking out the documentation for Mocha for more in depth configurations.

Has this tutorial been useful? - Please give me feedback via my twitter handle @dylanrhysscott!


Featured Image Credit: Code and Unicorns