28 Apr 2014

Mocking a function that returns a (bluebird) Promise

With Sinon.JS mocking functions are quite easy. Here is how to stub a function that returns a Promise.

Demonstrated with a potato quality example. Imagine the following code is in a file named db.js

var Promise = require('bluebird');

module.exports.query = function query(q) {
  return Promise.resolve([
    {
      username: 'bulkan',
      verified: true
    }
  ])
}

Using bluebird we simulate a database query which returns a Promise that is resolved with an Array of Objects.

Imagine the following code located in users.js;

var db = require('./db');

module.exports.getVerified = function getVerified(){
  return db.query('select * from where verified=true');
}

The mocha unit test for the above which stubs out db.query that is called in users.js;

var db = require('./db')
  , should  = require('chai').should()
  , sinon = require('sinon')
  , users;

describe('Users', function(){
  var sandbox, queryStub;

  beforeEach(function(){
    sandbox = sinon.sandbox.create();
    queryStub = sandbox.stub(db, 'query');
    users = require('./users');
  });

  afterEach(function(){
    sandbox.restore();
  });

  it('getVerified should return a resolved Promise', function(){
    queryStub.returns(Promise.reject('still resolved'));
    var p = users.getVerified();
    p.isResolved().should.be.true;
    return p;
  });
})

In the beforeEach and afterEach functions of the test we create a sinon sandbox which is slightly over kill for this example but it allows you to stub out a few methods without worrying about manually restoring each stub later on as you can just restore the whole sandbox as demonstrated in the afterEach.

There is one test case that tells the queryStub to return a Promise that is rejected. Then test that the promise that users.getVerified returns is resolved. Mocha now will wait until Promises that are returned from its to resolve.

Sorry about the potato quality example, been trying to think of a better example. Any suggestions ?

Hope this helps.

24 Apr 2014

Using mockery to mock modules for Node.js testing

In a previous article I wrote about mocking methods on the request module.

request also supports another workflow in which you directly call the imported module;

var request = require('request');

request({
  method: 'GET',
  url: 'https://api.github.com/users/bulkan'
}, function(err, response, body){
  if (err) {
    return console.err(err);
  }

  console.log(body);
})

You pass in an options object specifying properties like the HTTP method to use and others such as url, body & json.

Here is the example from the previous article updated to use request(options);

var request = require('request');

function getProfile(username, cb){
  request({
    method: 'GET',
    url: 'https://api.github.com/users/' + username
  }, function(err, response, body){
    if (err) {
      return cb(err);
    }
    cb(null, body);
  });
}

module.exports = getProfile;

Its not that big of a change. To unit test the getProfile function we will need to mock out request module that is being imported by the module that getProfile is defined in. This where mockery comes in. It allows us to change what gets returned when a module is imported.

Here is a mocha test case using mockery. This assumes that the above code is in a file named gh.js.

var sinon = require('sinon')
  , mockery = require('mockery')
  , should = require('chai').should();

describe('User Profile', function(){
  var requestStub, getProfile

  before(function(){
    mockery.enable({
      warnOnReplace: false,
      warnOnUnregistered: false,
      useCleanCache: true
    });

    requestStub = sinon.stub();

    // replace the module `request` with a stub object
    mockery.registerMock('request', requestStub);

    getProfile = require('./gh');
  });

  after(function(){
    mockery.disable();
  });

  it('can get user profile', function(done){
    requestStub.yields(null, {statusCode: 200}, {login: "bulkan"});

    getProfile('bulkan', function(err, result){
      if(err) {
        return done(err);
      }
      requestStub.called.should.be.equal(true);
      result.should.not.be.empty;
      result.should.have.property('login');
      done();
    });
  });
})

mockery hijacks the require function and replaces modules with our mocks. In the above code we register a sinon stub to be returned when require('request') is called. Then we configure the mock in the test using the method .yield on the stub to a call the callback function passed to request with null for the error, an object for the response and another object for the body.

You can write more tests

Hope this helps.