integration testing an api with mocha and superagent

This seemed to be the week people showed me testing triangles.

If you’ve never seen one then I’ve included one below.

This doesn’t give you the full range of tests but it does give you an overview.

The top of the triangle can includes acceptance tests, performance tests and security – anything that sits over the whole system.

The example I have used here is against a simple API, but tools like superagent could also run against the front-end of a website.

API

I’ve built a small API in express, this has a couple of routes that serve user data.

  • /api/users – this gives a list of users
  • /api/users/:id – this gives a 404 error if not found, or the requested user

These are self-contained integration tests, before all the tests a script starts a server.

Most of the time I’d be using a CI tool like Jenkins to run these tests against an actual service. Rather than having a static data file, this might be running against either a stub or database.

For these tests I’ve Mocha as a test runner and chai as an assertion library.

To drive requests to the website I have used superagent this can work both server and client side.

I did look at chai-http this mostly wraps superagent, so didn’t seem to add value.

Tests

With this api example there will be three tests

  1. test to get all users
  2. test to get user with valid id
  3. test to get user with invalid id

const {assert} = require('chai');
const superagent = require('superagent');
const status = require('http-status');
// include server
const server = require('../index');
// ser variables 
const port = process.env.PORT || 3000;
const validUserID = 'A0001';
const invalidUserID = 'B0001';

describe('/api/users', function() {
    let app;
 
    before(function() {
        // start server
        app = server(port);
    });
    
    after(function() {
        // close server
        app.close();
    });
    
    it('should return list of users when called', function(done) {  
        superagent
        .get(`http://localhost:${port}/api/users`)
        .end(function(err, res) {
            assert.ifError(err);
            assert.equal(res.status, status.OK);
            const result = JSON.parse(res.text);
            assert.lengthOf(result, 3);
            done();
        });
    });
    describe('/:id', function() { 
        it('returns user when valid user id is used', function(done) {
            superagent
            .get(`http://localhost:${port}/api/users/${validUserID}`)
            .end(function(err, res) {
                assert.ifError(err);
                assert.equal(res.status, status.OK);
                const result = JSON.parse(res.text);
                assert.equal(result.id, validUserID);
                done();
            });
        });

        it('returns 404 error with message when user does not exist', function(done) {
            superagent
            .get(`http://localhost:${port}/api/users/${invalidUserID}`)
            .end(function(err, res) {
                assert.equal(res.status, status.NOT_FOUND);
                const result = JSON.parse(res.text);
                assert.equal(result.message, `User ${invalidUserID} was not found.`);
                done();
            });
        });
    });
});

This will start up a server then run the tests against it, this could also be run on an external website, or against a HTML front-end.

The output of the tests are below.


  /api/users
    ✓ should return list of users when called
    /:id
      ✓ returns user when valid user id is used
      ✓ returns 404 error with message when user does not exist


  3 passing (109ms)