【问题标题】:How to stub Mongoose model methods in unit tests with sinon如何使用 sinon 在单元测试中存根 Mongoose 模型方法
【发布时间】:2020-06-26 14:33:05
【问题描述】:

我有一个方法getTask(),看起来像这样:

const Task = require('./model');
const User = require('../users/model')

module.exports = async function getTask (key) {
    const task = await Task.findOne({key}).exec()
    const user = await User.findById(task.author).exec()
    task.author = user
    return task
};

基本上,我只是通过key找到我的任务,在users集合中找到作者,将它们组合在一起,然后在我的端点中我这样称呼它:

app.get('/task/:key', async (req, res, next) => {
    const task = await getTask(req.params.key)
    res.status(200).json(task)
})

这就是我尝试测试此方法的方式:

const { expect } = require('chai')
const sinon = require('sinon')
const sinonTest = require('sinon-test')

const Task = require('./model');
const User = require('../users/model')
const getTask = require('./services')

const test = sinonTest(sinon);

describe('Task CRUD operations', () => {

    it('should find task and user and return together', test(async function() {
        const mockUser = new User({ id: '5e684ebacb19f70020661f44', username: 'testuser' });
        const mockTask = new Task({ id: '5e684ececb19f70020661f45', key: 'TEST-1', author: mockUser });

        const mockTaskFindOne = {
            exec: function () {
                return mockTask
            }
        };

        const mockUserFindById = {
            exec: function () {
                return mockUser
            }
        };

        this.stub(Task, 'findOne').returns(mockTaskFindOne)
        this.stub(User, 'findById').returns(mockUserFindById)
        const task = getTask('TEST-1')

        sinon.assert.calledWith(Task.findOne, { key: 'TEST-1' })
        sinon.assert.calledWith(User.findById, '5e684ebacb19f70020661f44')
        // TODO: assert returned task is what I expect
    }));
});

所以我想断言Task.findOne()User.findById() 是用正确的参数调用的,最后验证返回的task 数据。

现在唯一的最后一行在我断言 User.findById 被调用的地方不起作用:

  Task CRUD operations
spec.js:54
    1) should find task and user and return together
spec.js:88
  0 passing (17ms)
base.js:319
  1 failing
base.js:332
  1) Task CRUD operations
       should find task and user and return together:
     AssertError: expected findById to be called with arguments 
      at Object.fail (node_modules/sinon/lib/sinon/assert.js:107:21)
      at failAssertion (node_modules/sinon/lib/sinon/assert.js:66:16)
      at Object.assert.<computed> [as calledWith] (node_modules/sinon/lib/sinon/assert.js:92:13)
      at Context.<anonymous> (tasks/services.test.js:36:22)
      at callSandboxedFn (node_modules/sinon-test/lib/test.js:103:25)
      at Context.sinonSandboxedTest (node_modules/sinon-test/lib/test.js:131:26)
      at processImmediate (internal/timers.js:445:21)

我知道这与 Promises 等有关,但我完全是新手。

我做错了什么?

我的Task 模特:

const mongoose = require('mongoose');

let TaskSchema = new mongoose.Schema({
    key: String,
    author: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }
});

module.exports = mongoose.model('Task', TaskSchema);

我的User 模特:

const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');

let UserSchema = new mongoose.Schema({
    username: String,
    password: String
});

UserSchema.plugin(passportLocalMongoose);

module.exports = mongoose.model('User', UserSchema);

【问题讨论】:

    标签: node.js mongoose promise sinon stub


    【解决方案1】:

    基于getTask() 函数,User.findById() 通过task.author 调用。这意味着sinon.assert.calledWith(User.findById, mockUser)。而已。

    我创建了这个没有 sinon-test 的简单示例,以确保更改正确运行。

    // @file stackoverflow.spec.js
    const { expect } = require('chai');
    const sinon = require('sinon');
    
    const Task = require('./task.model');
    const User = require('./user.model');
    const getTask = require('./services');
    
    describe('Task CRUD operations', function () {
      const sandbox = sinon.createSandbox();
      it('should find task and user and return together', async function () {
        const mockUser = { id: '5e684ebacb19f70020661f44', username: 'testuser' };
        const mockTask = { id: '5e684ececb19f70020661f45', key: 'TEST-1', author: mockUser };
    
        const mockTaskFindOne = sandbox.stub(Task, 'findOne');
        mockTaskFindOne.returns({
          exec: () => mockTask,
        });
    
        const mockUserFindById = sandbox.stub(User, 'findById');
        mockUserFindById.returns({
          exec: () => mockUser,
        });
    
        const task = await getTask('TEST-1');
        // Verify task.
        expect(task).to.deep.equal(mockTask);
        // Verify stub.
        expect(mockTaskFindOne.calledOnce).to.equal(true);
        expect(mockTaskFindOne.args[0][0]).to.deep.equal({ key: 'TEST-1' });
        // This is the same with above.
        expect(mockTaskFindOne.calledWith({ key: 'TEST-1' })).to.equal(true);
        expect(mockUserFindById.calledOnce).to.equal(true);
        expect(mockUserFindById.args[0][0]).to.deep.equal(mockUser);
        // This is the same with above.
        expect(mockUserFindById.calledWith(mockUser)).to.equal(true);
    
        // Restore stub.
        mockTaskFindOne.restore();
        mockUserFindById.restore();
        // Restore sandbox.
        sandbox.restore();
      });
    });
    

    当我使用 mocha 运行它时通过。

    $ mocha stackoverflow.spec.js 
    
    
      Task CRUD operations
        ✓ should find task and user and return together
    
    
      1 passing (11ms)
    
    $
    

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2023-04-01
      • 2014-04-15
      • 2016-12-21
      • 2018-11-05
      • 2020-01-03
      • 2017-04-26
      • 2016-11-19
      • 1970-01-01
      • 2021-11-26
      相关资源
      最近更新 更多