Search code examples
node.jsunit-testingmongoosesinonsinon-chai

How to stub mongoose methods with multiple arguments in Sinon?


I am using Mongoose in Node.js and here is my DAO method.

function findPostsByCategoryId(categoryId, first, second) {
    var sortingOrd = { 'createdAt': -1 };
    return Post.find({ 'categoryId': categoryId }).sort(sortingOrd).skip(first).limit(second);
}

Now, I want to stub the method like this in my test case, using Sinon.

describe('findPostsByCategoryId', function () {
    it('should find post by category id', function () {
        var stub = sinon.stub(Post, 'find');
        stub.callsFake(() => {
            return Promise.resolve(posts);
        });
        postDao.findPostsByCategoryId(1, 2, 3).then(response => {
            assert.length(response, 1);
        })
            .catch((error) => {
                assert.isDefined(error);
            });
    });
});

This is returning me an error saying

TypeError: Post.find(...).sort is not a function.

Can you shed light on how to stub a DAO method that has multiple functions linked to it?


Solution

  • Here is the unit test solution:

    dao.js:

    const Post = require("./models/post");
    
    function findPostsByCategoryId(categoryId, first, second) {
      var sortingOrd = { createdAt: -1 };
      return Post.find({ categoryId: categoryId })
        .sort(sortingOrd)
        .skip(first)
        .limit(second);
    }
    
    module.exports = {
      findPostsByCategoryId,
    };
    

    ./models/post.js:

    // simulate Post model
    const Post = {
      find(where) {
        return this;
      },
      sort(...args) {
        return this;
      },
      skip(...args) {
        return this;
      },
      limit(n) {},
    };
    
    module.exports = Post;
    

    dao.test.js:

    const dao = require("./dao");
    const Post = require("./models/post");
    const sinon = require("sinon");
    const { expect } = require("chai");
    
    describe("Name of the group", () => {
      afterEach(() => {
        sinon.restore();
      });
      it("should pass", () => {
        sinon.stub(Post);
        Post.find.returnsThis();
        Post.sort.returnsThis();
        Post.skip.returnsThis();
        const mResponse = { rowCount: 100 };
        Post.limit.resolves(mResponse);
    
        return dao.findPostsByCategoryId(1, 2, 3).then((response) => {
          expect(response).to.be.eql(mResponse);
          sinon.assert.calledWithExactly(Post.find, { categoryId: 1 });
          sinon.assert.calledWithExactly(Post.sort, { createdAt: -1 });
          sinon.assert.calledWithExactly(Post.skip, 2);
          sinon.assert.calledWithExactly(Post.limit, 3);
        });
      });
    });
    

    Unit test result with coverage report:

      54920719
        ✓ should pass
    
    
      1 passing (14ms)
    
    -----------------|----------|----------|----------|----------|-------------------|
    File             |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
    -----------------|----------|----------|----------|----------|-------------------|
    All files        |    89.66 |      100 |    55.56 |    89.66 |                   |
     54920719        |      100 |      100 |      100 |      100 |                   |
      dao.js         |      100 |      100 |      100 |      100 |                   |
      dao.test.js    |      100 |      100 |      100 |      100 |                   |
     54920719/models |       40 |      100 |        0 |       40 |                   |
      post.js        |       40 |      100 |        0 |       40 |            4,7,10 |
    -----------------|----------|----------|----------|----------|-------------------|
    

    Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/54920719