【问题标题】:node.js - Apply sinon on mongodb unit testsnode.js - 在 mongodb 单元测试上应用 sinon
【发布时间】:2017-12-14 22:54:21
【问题描述】:

我用node-mongodb-native为mongodb实现了一个模型函数:

'use strict';

const mongo = require('mongodb');

class BlacklistModel {

    constructor(db, tenant_id, logger) {
        this._db = db;
        this._table = 'blacklist_' + tenant_id;
        this._logger = logger;
    }

    create(data) {
        return new Promise((resolve, reject) => {
            const options = {
                unique: true,
                background: true,
                w: 1
            };
            this._db.collection(this._table).ensureIndex({ phone: 1 }, options, (err) => {
                if (err) {
                    this._logger.error(err);
                    reject(err);
                } else {
                    const datetime = Date.parse(new Date());
                    data._id = new mongo.ObjectID().toString();
                    data.createdAt = datetime;
                    data.updatedAt = datetime;
                    this._db.collection(this._table).insertOne(data, (err) => {
                        if (err) {
                            this._logger.error(err);
                            reject(err);
                        } else {
                            resolve(data);
                        }
                    });
                }
            });
        });
    }

}

module.exports = BlacklistModel;

现在我想为它编写单元测试,考虑 3 种情况:

  • 插入成功
  • 由于违反唯一索引而失败
  • 由于失去连接而失败

考虑到这些,这是我的测试:

'use strict';

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const expect = chai.expect;

const BlacklistModel = require('../../model/blacklist');

const mongo_url = require('../../config/mongodb');
const MongoClient = require('mongodb').MongoClient;

const logger = require('../../config/logger');

const data = {
    name: 'admin'
};

describe('Model: Blacklist', () => {

    let Blacklist;
    let connected = false;
    let test_db;

    const connect = () => new Promise((resolve, reject) => {
        MongoClient.connect(mongo_url, (err, db) => {
            if (err) {
                reject(err);
            } else {
                Blacklist = new BlacklistModel(db, 'test', logger);
                connected = true;
                test_db = db;
                resolve();
            }
        });
    });

    before(() => connect());

    describe('create', () => {
        let id;
        beforeEach(() => connected ?
            null : connect());
        it('Should return an inserted document', () => {
            return Blacklist.create(data).then(
                (result) => {
                    expect(result._id).to.be.a('string');
                    expect(result.name).to.equal(data.name);
                    expect(result.createdAt).to.be.a('number');
                    expect(result.updatedAt).to.be.a('number');
                    id = result._id;
                });
        });
        it('Should fail to insert a blacklist with the same name', () => {
            const promise = Blacklist.create(data).then(
                (result) => {
                    id = result._id;
                    return Blacklist.create(data);
                });
            return expect(promise).to.be.rejected;
        });
        it('Should fail due to lost connection', () => {
            return test_db.close(true).then(() => {
                connected = false;
                return expect(Blacklist.create(data)).to.be.rejected;
            });
        });
        afterEach(() => connected ?
            Blacklist.delete(id) : connect().then(() => Blacklist.delete(id)));
    });

});

我在测试中调用真正的函数,在我看来,这在运行时看起来很尴尬且耗时,以避免副作用。但是目前除了更改测试数据库之外,我还没有提出任何其他想法。有没有办法使用sinon?我已经阅读了几篇关于sinon、spy、stub 和 mock 的博客,但很难理解和区分它们。我如何将它们应用到这些测试中?

【问题讨论】:

    标签: javascript node.js mongodb unit-testing sinon


    【解决方案1】:

    您当前编写的是集成测试,用于测试节点服务器和 mongo db 数据库之间的交互。尽管这些测试比模拟单元测试更耗时,但它们实际上提供了更多的价值。针对稳定的 MongoDB 实例运行查询可确保您的查询按计划运行,并且您的应用程序正确响应结果,请参阅:How to unit test a method which connects to mongo, without actually connecting to mongo?

    如果您想测试操作数据的 javascript 函数,而不是服务器和数据库之间的交互。我建议您从 mongodb 查询逻辑中重构此代码并对其进行单元测试。或者,当您使用类时,您应该能够使用模拟数据库库覆盖 _db 属性。这只是一个具有模仿您当前使用的 mongo 库的方法的对象。或者您可以使用 sinon 将这些方法存根,并用返回已知结果的方法替换它们,请参阅http://sinonjs.org/releases/v1.17.7/stubs/

    试试这样的:

    var ensureIndex = { ensureIndex: sinon.stub() }
    sinon.stub(db, 'collection').returns(ensureIndex)
    
    var blackList; 
    
    describe('Model: Blacklist', () => {
    
      beforeEach(() => {
        var blackList = new BlacklistModel(db, id, logger);
      })
      it('test' => { 
        blackList.create(data).then(() => {
          // some test here
          db.collection.calledWithMatch('some match')
        })
    
      })
    })
    

    【讨论】:

    • 我仍然很困惑如何使用sinon 来存根这些方法。你能根据上面的这些代码给我写一个例子吗?
    • 非常感谢,确实有帮助
    【解决方案2】:

    一种简单的方法是存根并返回自定义对象。
    通过这种方式,您还可以通过检查存根函数的参数和返回值来验证功能。
    这是我的例子

    // your class
    class TestCase{
       constructor(db){
          this.db = db;
       }
       method1(args1){
          this.db.insertOne(args1)
       }
       method2(args2){
          this.db.f(args2)
       }
    }
    
    // test file
    const sinon = require('sinon');
    
    const sandbox = sinon.createSandbox();
    const stubInsertOne = sandbox.stub();
    const stubFindOne = sandbox.stub();
    const stubMongo = {
      insertOne: stubInsertOne,
      findOne: stubFindOne
    }
    describe("TestCase", ()=>{
      beforeEach(()=>{
         // reset the sandbox or the stub result is polluted
         sandbox.reset();
      })
    
      it("method1 test", ()=> {
          stubInsertOne.resolves("what ever you want to mock return value");
          
          const testCase = new TestCase(stubMongo);
          testCase.method1();
      })
      .....
    })
    

    缺点是您必须手动存根 mongodb 中使用的每个函数调用。

    【讨论】:

      猜你喜欢
      • 2018-11-05
      • 2023-03-10
      • 2021-09-27
      • 2017-06-09
      • 1970-01-01
      • 2023-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多