{ Testing JSON APIs. }

Objectives:

By the end of this chapter, you should be able to:

  • Write code to test API endpoints
  • Use mocha, chai and supertest to write unit tests

Testing Intro

So far we have seen how to test with Mocha and Chai, but we need another tool to help us make requests and recieve responses. The additional module we'll use is supertest.

Since we are using testing libraries, these are not dependencies that will be using in production. Therefore, when we install these using npm we want to make sure to run npm install --save-dev rather than npm install --save, so that we do save them to our package.json, but do not install them in production. When you do this, you should see that your package.json consists of two sets of dependencies: dependencies and devDependencies.

To use mocha globally we can install it with npm install -g mocha. If you are getting errors with this installation you can use sudo npm install -g mocha. Since installing with sudo is not the best option, you can read more about how to fix permissions here if you would like to install without sudo going forward.

Building our Application with Mocha / Chai / Supertest

Since we will be using a database as well, we need to think about that configuration. So let's build off of our pets application, but install some addition development dependencies

cd pets-app-api
touch app.js
npm install --save-dev mocha chai supertest
mkdir test
touch test/routesSpec.js

Testing with Mocha, Chai and Supertest

To get started with supertest, we need to include a bit of information in our routesSpec.js:

// we will use supertest to test HTTP requests/responses
const request = require('supertest');
// we also need our app for the correct routes!
const app = require('../app');
// make sure we use chai
var expect = require("chai").expect;
// make sure we are using our models
var mongoose = require("mongoose");
mongoose.Promise = Promise;
var db = require('../models/');

In order to do this though, we need to make sure to add the following to our app.js:

module.exports = app;

This enables us to export our application for testing. Now that we have that set up, here is what a sample route test might look like:

describe('GET /', function() {
    it('responds with JSON', function(done) {
        request(app)
            .get('/')
            .expect('Content-Type', /json/)
            .expect(200, done);
    });
});

Testing Create

To test POST requests, we need to specify that we are posting a form as well as some form data which we will send to the server.

describe('POST /pets', function(){

  var newPet = {name: 'Bob'};

  it('responds successfully', function(done) {
    request(app)
      .post('/pets')
      .type('form')
      .send(newPet)
      .expect('Content-Type', /json/)
      .expect(200, done);
  });
})

Testing authentication and authorization

Now that we have a basic idea of how to test our CRUD operations, let's add some additional tests to make sure that users who are logged in (authenticated) can only perform CRUD operations on specific records (authorized).

// let's make an object to store our information
var auth = {};

function loginUser(auth) {
    return function(done) {
        request(app)
            .post('/api/auth/login')
            .send({
                username: 'test',
                password: 'secret'
            })
            .expect(200)
            .end(onResponse);

        function onResponse(err, res) {
            // let's add the token so that we can use it for testing later!
            auth.token = res.body.token;
            return done();
        }
    };
}

beforeEach(function(done){
    db.User.create({username:'test', password:'secret'}).then(function(user) {
        // let's have a current user!
        auth.current_user = user;
        done();
    });
});

// and then log them in
beforeEach(loginUser(auth));

afterEach(function(done){
    db.User.remove({}).then(function() {
      done();
    });
});

Let's now see what a couple OF routes might look like when tested:

describe('PATCH /users/:id', function() {
    it('responds with JSON', function(done) {
        request(app)
            .patch(`/api/users/${auth.current_user.id}`)
            .send({
                username:'bob'
            })
            .set('Authorization', 'Bearer: ' + auth.token)
            .expect(200, done);
    });
});

describe('DELETE /users/:id', function() {
    it('responds with JSON', function(done) {
        request(app)
            .delete(`/api/users/${auth.current_user.id}`)
            .set('Authorization', 'Bearer: ' + auth.token)
            .expect(204, done);
    });
});

Sample application

You can see a sample application with testing a JSON API here.

When you're ready, move on to JSON API Exercises

Continue