Search code examples
node.jsmocha.jssinonsinon-chai

Testing with Sinon-Chai that an individual callback in a Node JS Express route with multiple callbacks is called


I'm relatively new to Node and Sinon. This application was made with Express, and I'm using Mocha, Chai, and Sinon. Using Sinon-Chai, I'm POST testing routes in Express with multiple callbacks, and can't figure out how to check second and subsequent callbacks.

The route inside my index.js is:

var controller = require('./example.controller');
var validator = require('./example.validator');
var router = express.Router();

router.post('/', validator.create, controller.create);

In my validator.js is the validator.create which checks the submitted parameter:

exports.create = function(req, res, next) {
  var valid = true;
  var errorMessages = [];

  if (req.body.name) {
    patt = /[^a-zA-Z0-9 !@#$%^&*()_+\-=\[\]{};':]/g;
    if (patt.test(req.body.name)) {
      valid = false;
      errorMessages.push("Parameter is not alphanumeric");
    }
  }

  if (valid == false) {
    return res.status(400).json(errorMessages);
  }

  next();
}

In my controller.js is the controller.create which creates a entry in the DB:

exports.create = function(req, res) {
    return Example.create(req.body)
        .then(baseController.respondWithResult(res, 201))
        .catch(baseController.handleError(res));
}

The Sinon-Chai tests in my index.spec.js:

var proxyquire = require('proxyquire').noPreserveCache();   

var exampleCtrlStub = {
    create: 'exampleCtrl.create',
};

var exampleValidatorStub = {
    create: 'exampleValidator.create'
}

var routerStub = {
    get: sinon.spy(),
    put: sinon.spy(),
    patch: sinon.spy(),
    post: sinon.spy(),
    delete: sinon.spy()
};

var exampleIndex = proxyquire('./index.js', {
    express: {
        Router() {
            return routerStub;
        }
    },
    './example.controller': exampleCtrlStub,
    './example.validator': exampleValidatorStub
});

describe('POST /api/examples', function() {
    it('should route to example.validator.create', function() {
        routerStub.post
            .withArgs('/', 'exampleValidator.create')
            .should.have.been.calledOnce;
    });
});


describe('POST /api/examples', function() {
    it('should route to example.controller.create', function() {
        routerStub.post
            .withArgs('/', 'exampleCtrl.create')
            .should.have.been.called;
    });
});

Though expecting both tests to pass, the first test (validator.create) passes but the second one (controller.create) fails. I've not been able to find a way to test that the controller.create is called.


Solution

  • in the second test, we can't skip the first validator argument using withArgs. The test is failed because it is looking for a method with this signature which is not exist in source.

    router.post('/', controller.create);
    

    withArgs always start with the first then second argument, etc. So, the solution is to include the validator in the test

    routerStub.post
      .withArgs('/', 'exampleValidator.create', 'exampleCtrl.create')
      .should.have.been.called;
    

    Reference:

    Hope it helps