cat dad
cat dad

Reputation: 318

How to test stubbed database queries

I have a Sails.Js controller that looks like this

module.exports = {
  confirmID: function(req,res) {
    var uid = req.params.id;
     User.findOne({id:uid}).exec(function(err,user) {
       // ...
     });
  }
}

where User is a sails-postgres model. I have tried testing it with mocha, sinon and supertest with a test like this

describe('Controller', function() {
  var sandbox;

  before(function() {
    sandbox = sinon.sandbox.create();
    sandbox.stub(User, 'findOne');
  });

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

  describe('GET /confirmid/:id', function() {

    it('should do something', function(done) {
      request(sails.hooks.http.app)
        .get('/confirmid/123')
        .expect(200)
        .end(function(err,res) {
          sandbox.fakes[0].called.should.be.true;
          done();
        });
  });
});

If I leave it at that it errors out because exec is called on undefined, but I can't seem to stub the nested exec method without either errors or the test hanging. Is there a way to stub a series of method calls such as .find().exec()? Or am I best to just leave this to integration tests where I can test it with an actual database?

Upvotes: 3

Views: 2895

Answers (3)

smileham
smileham

Reputation: 1460

For people looking for other options to stub or mock waterline models, I've found the following four options:

After evaluating each one, I've decided on sails-mock-models because it is easy to understand and seems the most used sails mocking library according to npm: https://www.npmjs.com/package/sails-mock-models

Hope this helps someone!


Update: I'm still using sails-mock-models, and it is quite easy, but there are a few drawbacks such as it fails to return promises that are taken into a q.all(promiseArray).then() call. If I get around to investigating the other options or find a workaround, I will post it here.

Upvotes: 2

tswaters
tswaters

Reputation: 5773

This will only work for queries that use exec and it overloads all exec calls so if you try to return an error and you have, say, a controller with a policy out front, and the policy does a database lookup, you'll likely go into error there prior to hitting the controller code you intended to test.... that can be fixed with stub.onCall(x), but it is still a bit precarious.

Warnings aside, here's how I've done this in the past:

var path = require('path');
var sinon = require('sinon');

var Deferred = require(path.join(
  process.cwd(),
  'node_modules/sails',
  'node_modules/waterline',
  'lib/waterline/query/deferred'
));

module.exports = function () {
  return sinon.stub(Deferred.prototype, 'exec');
};

Assuming you have the following service, MyService:

module.exports.dbCall = function (id, cb) {

  Model.findOne(id).exec(function (err, result) {

    if (err) {
      sails.log.error('db calls suck, man');
      return cb(err, null);
    }

    cb(null, result);

  });

};

You can test the error case like so:

before(function () { 
  stub = databaseStub(); 
});

afterEach(function () { 
  stub.reset();
});

after(function () {
  stub.restore(); 
});

it('should return errors', function (done) {

  stub.onCall(0).callsArgWith(0, 'error');

  MyService.dbCall(1, function (err, results) {
    assert.equal(err, 'error');
    assert.equal(results, null);
    done();
  });

});

Upvotes: 0

wxactly
wxactly

Reputation: 2470

Assuming that you really want to stub (not just spy) - you want to control what the query resolves to as opposed to simply knowing whether the query was executed. Here's what I'm using to stub sails/waterline query methods. Something like...

var stubQueryMethod = require('stubQueryMethod');

describe('Controller', function() {
  before(function() {
    stubQueryMethod(User, 'findOne', {
      id: 123,
      name: 'Fred Fakes'
    });
  });

  after(function() {
    User.findOne.restore();
  });

  describe('GET /confirmid/:id', function() {
    it('should do something', function(done) {
      request(sails.hooks.http.app)
        .get('/confirmid/123')
        .expect(200)
        .end(function(err,user) {
          user.should.have.property('name', 'Fred Fakes');
          done();
        });
    });
  });
});

Source: https://gist.github.com/wxactly/f2258078d802923a1a0d

Upvotes: 3

Related Questions