簡體   English   中英

node.js - 在 mongodb 單元測試上應用 sinon

[英]node.js - Apply sinon on mongodb unit tests

我用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 的博客,但很難理解和區分它們。 我如何將它們應用到這些測試中?

您目前編寫的是集成測試,用於測試節點服務器和 mongo 數據庫之間的交互。 盡管這些測試比模擬單元測試更耗時,但它們實際上提供了更多的價值。 針對穩定的 MongoDB 實例運行查詢可確保您的查詢按計划運行並且您的應用程序正確響應結果請參閱: 如何對連接到 mongo 的方法進行單元測試,而無需實際連接到 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')
    })

  })
})

一種簡單的方法是存根並返回自定義對象。
通過使用這種方式,您還可以通過檢查存根函數的參數和返回值來驗證功能。
這是我的例子

// 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 中使用的每個函數調用進行存根。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM