如何在 Sinon 中存根具有多个参数的猫鼬方法?

How to stub mongoose methods with multiple arguments in Sinon?

我在 Node.js 中使用 Mongoose,这是我的 DAO 方法。

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

现在,我想在我的测试用例中使用 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);
            });
    });
});

这给我返回一个错误

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

您能否阐明如何对链接有多个函数的 DAO 方法进行存根?

要对这样链接的函数进行单元测试,只需链接 stubspy 实例并验证它们是否使用预期值调用:

it('should find post by category id', function () {
  const limitSpy = sinon.spy();
  const skipStub = sinon.stub().returns({ limit: limitSpy });
  const sortStub = sinon.stub().returns({ skip: skipStub });
  const findStub = sinon.stub(Post, 'find').returns({ sort: sortStub });

  postDao.findPostsByCategoryId(1, 2, 3);

  sinon.assert.calledWithExactly(findStub, { 'categoryId': 1 });  // SUCCESS
  sinon.assert.calledWithExactly(sortStub, { 'createdAt': -1 });  // SUCCESS
  sinon.assert.calledWithExactly(skipStub, 2);  // SUCCESS
  sinon.assert.calledWithExactly(limitSpy, 3);  // SUCCESS
});

单元测试解决方案如下:

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);
    });
  });
});

包含覆盖率报告的单元测试结果:

  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 |
-----------------|----------|----------|----------|----------|-------------------|

源代码:https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/Whosebug/54920719