Acceptance testing with supertest

supertest is a Super-agent driven library for testing node.js HTTP servers using a fluent API

supertest works by starting an express application and running test against it. This type of testing is often referred to as end-to-end testing or some times integration testing. This style of testing has the benefit of testing your public api where unit test test individual internal units.

  1. To begin add supertest to your package.json file under "devDependencies"

    Your "devDependencies" should look like this now:

    package.json

     "devDependencies": {
       "mocha": "*",
       "should": ">= 0.0.1",
       "supertest": "0.3.x"
     }
    
  2. Updated your dependencies

     npm install
    
  3. supertest takes the express app that it will be testing as an argument, therefore we must export our app from app.js

    app.js

     //... imports
    
     var app = exports.app = express();
    
     //...
    
  4. After adding supertest a bug was discovered in our test/utils.js file. When supertest instantiates our express app a race condition can occur that cause our test to fail. The race condition is caused by the mocha test runner attempting to run test while the mongoose connection is opening or closing. To prevent this we can check the status of the connection. If it is opening or closing we can wait and retry. To fix this bug update the contents of your test/utils.js file to match:

    test/utils.js

     'use strict';
    
     // Modified from https://github.com/elliotf/mocha-mongoose
    
     var config = require('../config');
     var mongoose = require('mongoose');
    
     // ensure the NODE_ENV is set to 'test'
     // this is helpful when you would like to change behavior when testing
     process.env.NODE_ENV = 'test';
    
     beforeEach(function (done) {
    
       function clearDB() {
         for (var i in mongoose.connection.collections) {
           mongoose.connection.collections[i].remove();
         }
         return done();
       }
    
       function reconnect() {
         mongoose.connect(config.db.test, function (err) {
           if (err) {
             throw err;
           }
           return clearDB();
         });
       }
    
       function checkState() {
         switch (mongoose.connection.readyState) {
         case 0:
           reconnect();
           break;
         case 1:
           clearDB();
           break;
         default:
           process.nextTick(checkState);
         }
       }
    
       checkState();
     });
    
     afterEach(function (done) {
       mongoose.disconnect();
       return done();
     });
    
    
  5. Write your first acceptance test:

    To go along with our first unit test we will will create an route that does addition. Give the route /add/1/1 we want to return 2. Let's add the test to the test/addition.js file:

    test/addition.js

     'use strict';
    
     // import the moongoose helper utilities
     var utils = require('./utils');
     var request = require('supertest');
     var should = require('should');
     var app = require('../app').app;
    
     describe('addition', function () {
       //... previous test
       it('should return 2 given the url /add/1/1', function (done) {
         request(app)
           .get('/add/1/1')
           .expect(200)
           .end(function (err, res) {
             should.not.exist(err);
             parseFloat(res.text).should.equal(2);
             done();
           });
       });
     });
    

    Check the test runner. The test will fail with:

     ✖ 1 of 6 tests failed:
    
     1) addition should return 2 given the url /add/1/1:
        AssertionError: expected [Error: expected 200 "OK", got 404 "Not Found"] to not exist
    

    Thats because we don't have a route to handle /add.

  6. Create a route to handle /add

    app.js

     //... previous code
    
     app.get('/', routes.index);
     app.get('/users', user.list);
    
     app.get('/add/:first/:second', function (req, res) {
       // convert the two values to floats and add them together
       var sum = parseFloat(req.params.first) + parseFloat(req.params.second);
       res.send(200, String(sum));
     });
    
     //... previous code
    
  7. Check the test runner. Test will now be passing:

     addition
       ✓ should add 1+1 correctly
       ◦ should return 2 given the url /add/1/1: GET /add/1/1 200 1ms - 1
       ✓ should return 2 given the url /add/1/1
    
     Users: models
       #create()
         ✓ should create a new User
       #hashPassoword()
         ✓ should return a hashed password asynchronously
       #comparePasswordAndHash()
         ✓ should return true if password is valid
         ✓ should return false if password is invalid
    
     ✔ 6 tests complete (139 ms)
    

Resources

  • supertest - Super-agent driven library for testing node.js HTTP servers using a fluent API

Comments