【问题标题】:Making Mocha test work for my db module使 Mocha 测试适用于我的 db 模块
【发布时间】:2013-03-13 08:41:00
【问题描述】:

我是 Mocha 的新手,对 Node/Express 仅有一点经验。当我通过我的 Express 应用程序访问它时,我的 DbProvider 模块运行良好(mongodb)。现在我想测试它。我已经阅读了 Mocha 网站和一些我可以找到的教程。但是我很难找到一个我可以遵循的真实示例(非常感谢任何链接!)。

这是我编写测试文件的失败尝试:

var DbProvider = require('../db').DbProvider;
var assert     = require('assert');
var dbProvider = new DbProvider('localhost', 27017, 'mydb');
var util       = require('util');

console.log(util.inspect(dbProvider));

describe('DbProvider', function(){
  describe('findAllNotes', function(){
    it('should return some notes', function(){
      dbProvider.findAllNotes({}, function (err, result){
        assert(result.length > 0);
      });
    })
  })
})

我得到的输出是这样的:

$ mocha
{}

  ✖ 1 of 1 test failed:

  1) DbProvider findAllNotes should return some notes:
     TypeError: Cannot call method 'collection' of undefined
      at DbProvider.doOperation (/Users/frode/Node/json/db.js:46:11)
      at DbProvider.findAllNotes (/Users/frode/Node/json/db.js:56:8)
      at Context.<anonymous> (/Users/frode/Node/json/test/test.js:15:18)
(cutting out the rest)

我好像没有成功创建dbProvider。这在我的应用程序中完美运行......我怎样才能使它工作? (也许还有:我的设置方式一般好吗?)

编辑:这是 db.js 文件:

// Database related
'use strict';

var MongoClient       = require('mongodb').MongoClient;
var BSON              = require('mongodb').BSONPure;
var ObjectID          = require('mongodb').ObjectID;
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
var Validator         = require('validator').Validator
var fieldMaxLength    = 1024;
//var util              = require('util');

var DbProvider = function(host, port, database) {
  var dbUrl = "mongodb://"+host+":"+port+"/"+database;
  var self = this;
  MongoClient.connect(dbUrl, function(err, db) {
    self.db = db;
  });
};

// Do some basic validation on the data we get from the client/user
var validateParams = function(params, callback) {
  // Let´ do a quick general sanity check on the length on all fields
  for(var key in params) {
    if(params[key].length > fieldMaxLength) callback(new Error('Field ' + key + ' is too long.'));
  }
  // and the let us check some specific fields better
  if (params._id) {
    if(checkForHexRegExp.test(params._id)) {
      // In case of '_id' we also need to convert it to BSON so that mongodb can use it.
      params._id = new BSON.ObjectID(params._id);
    } else {
      var err = {error: 'Wrong ID format'};
    }
  }
  if(err) callback(err);
}

// Generalized function to operations on the database
// Todo: Generalize even more when user authenication is implemented
DbProvider.prototype.doOperation = function(collection, operation, params, callback) {
  validateParams(params, callback);
  var operationCallback = function(err, result) {
    callback(err, result);
  };
  this.db.collection(collection, function(err, collection) {
    if(operation==='find') {
      collection.find().toArray(operationCallback);
    } else {
      collection[operation](params, operationCallback);
    }
  });
}

DbProvider.prototype.findAllNotes = function(params, callback) {
  this.doOperation('notes', 'find', params, callback);
};

DbProvider.prototype.findNoteById = function(params, callback) {
  this.doOperation('notes', 'findOne', params, callback);
};

DbProvider.prototype.saveNote = function(params, callback) {
  params.created_at = new Date();
  this.doOperation('notes', 'save', params, callback);
};

DbProvider.prototype.deleteNote = function(params, callback) {
  this.doOperation('notes', 'remove', params, callback);
};

DbProvider.prototype.findUser = function(params, callback) {
  this.doOperation('users', 'findOne', params, callback);
};

exports.DbProvider = DbProvider;

解决方案:

在 Benjamin 告诉我处理 mongodb 连接到数据库的异步性质后,并受到他关于如何调整代码的建议的启发,我将构造函数 DbProvider 拆分为两部分。第一部分,构造函数 DbProvider 现在只是将 db 参数保存到一个变量中。第二部分,一个新函数,DbProvider.connect 执行实际的异步连接。见下文。

var DbProvider = function(host, port, database) {
  this.dbUrl = "mongodb://"+host+":"+port+"/"+database;
};

DbProvider.prototype.connect = function(callback) {
  var self = this;
  MongoClient.connect(this.dbUrl, function(err, db) {
    self.db = db;
    callback();
  });
};

所以我现在可以像这样进行 Mocha 测试(并且异步测试还需要包含“完成”,就像您在下面的代码中看到的那样):

var assert     = require('assert');
var DbProvider = require('../db').DbProvider;
var dbProvider = new DbProvider('localhost', 27017, 'nki');

describe('DbProvider', function(){
  describe('findAllNotes', function(){
    it('should return some notes', function(done){
      dbProvider.connect(function(){
        dbProvider.findAllNotes({}, function (err, result){
          assert(result.length > 0);
          done();
        });
      });
    })
  })
})

请注意,实际测试(“应该返回一些注释”)并不值得骄傲。我在这里想要的是进行设置,以便我能够测试一些东西。现在我终于可以做到这一点,我需要编写好的测试(在拥有测试数据库的行中,清除它,测试插入文档,测试搜索文档等等......)。

在我的 Express 应用程序中,我曾经这样设置数据库:

var DbProvider    = require('./db').DbProvider;
// Setup db instance
var dbProvider = new DbProvider(
  process.env.mongo_host       || 'localhost',
  process.env.mongo_port       || 27017,
  process.env.mongo_db         || 'nki'
);

现在我做同样的事情,但另外,我调用了新的连接函数:

// Connect to db. I use (for now) 1 connection for the lifetime of this app.
// And I do not use a callback when connecting here (we do in the testing)
dbProvider.connect(function(){});

Benjamin 实际上指出,在 Express 应用程序中像这样设置数据库可能没问题,但不是最佳做法。但是,在我弄清楚真正的最佳实践是什么之前,我将保留这段代码。以下是我找到的有关该主题的几个链接(但我还没有得出自己将如何解决它的结论):
What's the best practice for MongoDB connections on Node.js?
[node-mongodb-native] MongoDB Best practices for beginner

如果你喜欢,非常欢迎你在 github 上关注/fork/whatever 这个项目。我的目标是尽我所能将其做好生产准备。链接是 https://github.com/frodefi/node-mongodb-json-server

【问题讨论】:

  • 没有../db 的代码我只是在猜测,但看起来您没有考虑创建数据库连接的异步性质。请贴出DbProvider的实现。
  • @frodefi 乍一看,您的“参数”似乎是您在 findAllNotes 中的“回调”。 s
  • @BenjaminGruenbaum,在测试中我这样称呼它: dbProvider.findAllNotes({}, function (err, result)... {} 是参数(但为空,我有参数这个功能只是为了让我可以使 doOperation 适合所有用法)。无论如何,console.log(util.inspect(dbProvider)); 表明 dbProvider 不是首先创建的(据我所知)......我在我的 express 应用程序中使用相同的调用来调用它,它在那里工作(如果我正确复制/粘贴,并且据我所知)......
  • @frodefi 是的,你得到它是因为 MongoClient.connect 是异步的,请参阅我的回答 :)

标签: node.js express mocha.js


【解决方案1】:

这是我用的:https://github.com/arunoda/mocha-mongo

它有一套 mongodb 的测试助手

【讨论】:

  • 感谢您的提示,阿鲁诺达 :)
【解决方案2】:

MongoClient.connectasynchronous

来自文档:

callback (function) – 这将在执行此方法后被调用。如果发生错误,第一个参数将包含 Error 对象,否则为 null。而第二个参数将包含初始化的 db 对象,如果发生错误,则为 null。

这意味着DbProvider.db 尚未在测试中设置,这就是您获得undefined 的原因。

在这里:

MongoClient.connect(dbUrl, function(err, db) {
    self.db = db;
});

您告诉它“连接发生后更新 self.db”,这至少是在此事件循环之后的一个事件循环滴答声(但可能更多)。在您的 mocha 代码中,您在创建 DbProvider 实例后立即执行您的 .describe.it 方法,这意味着它尚未初始化。

我建议您重构 DbProvider 以返回回调而不是构造函数。也许是这样的:

var getDbProvider = function(host, port, database,callback) {
  var dbUrl = "mongodb://"+host+":"+port+"/"+database;
  MongoClient.connect(dbUrl, function(err, db) {
    self.db = db;
    callback(db);
  });
};

这也意味着将所有DBProvider 方法移动到一个对象(也许回调将返回一个dbprovider 对象而不仅仅是一个db?)。

使用单元测试解决了另一个错误:)

【讨论】:

  • 所以所有(数据库相关的)测试都将在这个回调中,对吧?回调还没有完全融入我的基因中......
  • 是的,所有数据库相关的测试都会在这个回调中。这是解决此问题的唯一正确方法。
  • 我应该只在测试中这样做,还是应该在 Express 应用程序中也做类似的事情?我猜我不应该,因为创建数据库连接并不是每次处理请求时都会发生的事情。
  • 这也是您需要在您的 Express 应用程序中执行的操作,您可以移动您的数据库连接,但您必须等待它可用,然后再处理它
  • 嗯。这是否意味着我一直遵循的本教程是错误的? howtonode.org/express-mongodb 中间有 articleprovider-mongo.js 跟 app.js。据我在 app.js 中看到的,那里没有这样的结构吗?还是我误会了什么?
猜你喜欢
  • 2019-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-03
  • 1970-01-01
  • 2021-01-23
相关资源
最近更新 更多