14

I get how to stub Mongoose models (thanks to Stubbing a Mongoose model with Sinon), but I don't quite understand how to stub calls like:

myModel.findOne({"id": someId})
    .where("someBooleanProperty").equals(true)
    ...
    .exec(someCallback);

I tried the following:

var findOneStub = sinon.stub(mongoose.Model, "findOne");
sinon.stub(findOneStub, "exec").yields(someFakeParameter);

to no avail, any suggestions?

Community
  • 1
  • 1
Nepoxx
  • 4,849
  • 5
  • 42
  • 61

6 Answers6

28

I've solved it by doing the following:

var mockFindOne = {
    where: function () {
        return this;
    },
    equals: function () {
        return this;
    },
    exec: function (callback) {
        callback(null, "some fake expected return value");
    }
};

sinon.stub(mongoose.Model, "findOne").returns(mockFindOne);
Nepoxx
  • 4,849
  • 5
  • 42
  • 61
8

Take a look to sinon-mongoose. You can expects chained methods with just a few lines:

sinon.mock(YourModel).expects('findOne')
  .chain('where').withArgs('someBooleanProperty')
  .chain('exec')
  .yields(someError, someResult);

You can find working examples on the repo.

Also, a recommendation: use mock method instead of stub, that will check the method really exists.

Gon
  • 627
  • 7
  • 6
  • 1
    This gives me: "TypeError: sinon.stub(...).expects is not a function" – schw4ndi Oct 29 '17 at 23:14
  • 1
    @schw4ndi you're using `sinon.stub` instead of `sinon.mock`. Tell me if that doesn't solve your issue. – Gon Nov 02 '17 at 17:50
  • @OllyJohn that's how `sinon` works: you can expect on a mock but not on a stub. That's why I recommend to use `mock` instead of `stub` if you can expect something from the mocked call. – Gon Jul 06 '18 at 13:52
3

Another way is to stub or spy the prototype functions of the created Query (using sinon):

const mongoose = require('mongoose');

sinon.spy(mongoose.Query.prototype, 'where');
sinon.spy(mongoose.Query.prototype, 'equals');
const query_result = [];
sinon.stub(mongoose.Query.prototype, 'exec').yieldsAsync(null, query_result);
Kristofer Sommestad
  • 3,061
  • 27
  • 39
2

If you use Promise, you can try sinon-as-promised:

sinon.stub(Mongoose.Model, 'findOne').returns({
  exec: sinon.stub().rejects(new Error('pants'))
  //exec: sinon.stub(). resolves(yourExepctedValue)
});
Ping.Goblue
  • 576
  • 8
  • 12
2

I use promises with Mongoose and stub the methods like this:

const stub = sinon.stub(YourModel, 'findById').returns({
    populate: sinon.stub().resolves(document)
})

Then I can call it like:

const document = await YourModel.findById.populate('whatever');
Jani Siivola
  • 694
  • 7
  • 16
0

Using Multiple mongoose method stub using below code. Service code

 models.Match.findOne({ where: { id: ChangeInningInputType.id } })
      .then((match) => {
        if (!match) {
         throw Error('Match not found');
        }
        models.Match.update(
          { inning: ChangeInningInputType.inning },
          { where: { id: ChangeInningInputType.id } },
        );
        return resolve(match);
      })
      .catch((error) => {
        return reject(error);
      });

Testcase code

 it('Success test case for inning update', async () => {
    const bulkCreateStub = sinon
      .stub(models.Match, 'findOne')
      .resolves(inningResponse);

    const updateStub = sinon
      .stub(models.Match, 'update')
      .resolves(inningResponse);

    const aa = await changeInning(
      {},
      {
        ChangeInningInputType: inningRequest,
      },
    );
    console.log('updateStub===>', updateStub);
    expect(updateStub.calledOnce).to.equal(true);

    expect(bulkCreateStub.calledOnce).to.equal(true);
  });