node.js – 在mongodbunit testing中应用sinon

我使用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; 

现在我想写它的unit testing,考虑3例:

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

铭记这些,我的testing:

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

我在testing中调用了真正的函数,这在运行时看起来很尴尬和耗时,以至于我的愚见可以避免副作用。 但是现在除了改变一个testing数据库之外,我还没有提出任何其他的想法。 有没有办法使用sinon ? 我已经阅读了几篇有关sinon ,间谍,存根和模拟的博客,但很难理解和区分它们。 我怎么能在这些testing中使用它们?

你目前写的是集成testing,testing你的节点服务器和mongo db数据库之间的交互。 虽然这些testing比较费时,但是模拟unit testing实际上提供了更多的价值。 对一个稳定的MongoDB实例运行查询可以确保你的查询按计划运行,并且你的应用程序能够正确地响应结果。参见: 如何unit testing连接到mongo的方法,而不实际连接到mongo? 。

如果你想testing操作数据的JavaScript函数,而不是服务器和数据库之间的交互。 我build议你从mongodb查询逻辑重构出这个代码,并对它进行unit testing。 或者,如果您使用的是类,则应该可以使用模拟数据库库来覆盖_db属性。 这只是一个方法模仿你正在使用的mongo库的对象。 或者你可以使用sinon来存储这些方法,并用返回知道结果的方法replace它们,参见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') }) }) })