4

私はMochaを初めて使用し、Node/Expressの経験はほとんどありません。Expressアプリからアクセスすると、DbProviderモジュールは完全に機能します(mongodb)。そして今、私はそれをテストしたいと思います。私はモカのサイトと私が見つけたいくつかのチュートリアルを読みました。しかし、私がたどることができる実際の例を見つけるのに大きな問題があります(リンクは大歓迎です!)。

テストファイルを書き込もうとして失敗したのは次のとおりです。

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;

解決:

ベンジャミンがデータベースに接続するmongodbの非同期性を処理するように指示し、コードを適応させる方法についての彼の提案に触発された後、コンストラクター関数DbProviderを2つの部分に分割しました。最初の部分であるコンストラクターDbProviderは、dbパラメーターを変数に保存するだけです。2番目の部分である新しい関数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テストを作成できます(以下のコードに示すように、非同期テストにも「Done」が含まれている必要があります)。

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アプリでこのように設定することは問題ないかもしれないが、ベストプラクティスではないことを指摘しました。ただし、ベストプラクティスが実際に何であるかを理解するまで、このコードはそのままにしておきます。これが私が見つけた主題に関するいくつかのリンクです(しかし、私はまだそれを自分で解決する方法について結論を出していません):
Node.jsでのMongoDB接続のベストプラクティスは何ですか?および
[node-mongodb-native]MongoDB初心者向けのベストプラクティス

よろしければ、githubでこのプロジェクトをフォロー/フォーク/何でも歓迎します。私の目標は、できる限り本番環境に対応できるようにすることです。リンクは https://github.com/frodefi/node-mongodb-json-serverです

4

2 に答える 2

1

MongoClient.connect非同期です。

ドキュメントから:

コールバック(関数)–これはこのメソッドの実行後に呼び出されます。最初のパラメータには、エラーが発生した場合はErrorオブジェクトが含まれ、それ以外の場合はnullが含まれます。2番目のパラメーターには、初期化されたdbオブジェクトが含まれますが、エラーが発生した場合はnullが含まれます。

つまりDbProvider.db、テストでまだ設定されていないため、が取得されundefinedます。

ここに:

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

「接続が発生した後にself.dbを更新する」と言っています。これは、このイベントループティックの後に少なくとも1つのイベントループティックがあります(ただし、それ以上になる場合があります)。コードでは、インスタンスを作成した直後にメソッドとメソッドmochaを実行しています。つまり、インスタンスはまだ初期化されていません。.describe.itDbProvider

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メソッドをオブジェクトに移動することも意味します(おそらく、コールバックはdbproviderdbだけでなくオブジェクトを返しますか?)。

ユニットテストを使用して解決された別のバグ:)

于 2013-03-23T16:15:19.043 に答える
0

これは私が使用したものです:https ://github.com/arunoda/mocha-mongo

mongodbのテストヘルパーのセットがあります

于 2013-04-19T05:34:46.103 に答える