【问题标题】:Testing asynchronous middleware functionality with Mongoose使用 Mongoose 测试异步中间件功能
【发布时间】:2013-08-31 23:53:22
【问题描述】:

我在 Mongoose 中使用 save 中间件在执行某些操作时在数据库中创建活动日志。类似的东西

UserSchema.post("save", function (doc) {
    mongoose.model("Activity").create({activity: "User created: " + doc._id});
});

这似乎工作正常,但问题是我无法测试它,因为无法将 回调 传递给post(这可能没有意义)。我使用mocha 对此进行了测试:

User.create({name: "foo"}, function (err, user) {
    Activity.find().exec(function (err, act) {
        act[0].activity.should.match(new RegExp(user._id));
        done();
    });
});

问题是 Activity.create 在调用 .find 之前显然没有完成。我可以通过将.find 包裹在setTimeout 中来解决这个问题,但这对我来说似乎很老套。有没有办法测试异步猫鼬中间件操作?

【问题讨论】:

    标签: node.js mongodb mongoose mocha.js should.js


    【解决方案1】:

    异步保存后中间件显然将在 Mongoose 4.0.0 中可用:

    https://github.com/LearnBoost/mongoose/issues/787

    https://github.com/LearnBoost/mongoose/issues/2124

    目前,您可以通过猴子修补文档上的保存方法来解决此问题,以便它支持异步保存后中间件。以下代码在与您类似的情况下为我工作。

    // put any functions here that you would like to run post-save
    var postSave = [
      function(next) {
        console.log(this._id);
        return next();
      }
    ];
    
    var Model = require('mongoose/lib/model');
    
    // monkey patch the save method
    FooSchema.methods.save = function(done) {
      return Model.prototype.save.call(this, function(err, result) {
        if (err) return done(err, result);
    
        // bind the postSave functions to the saved model
        var fns = postSave.map(function(f) {return f.bind(result);});
        return async.series(fns,
          function(err) {done(err, result);}
        );
    
      });
    };
    

    【讨论】:

      【解决方案2】:

      不幸的是,没有办法以您想要的方式可靠地交错这两个异步函数(因为没有线程,您不能“暂停”执行)。它们可能会以不一致的顺序完成,这会让您遇到超时等解决方案。

      我建议您将事件处理程序连接到 Activity 类,以便当 Activity 写入/失败时,它会查看应记录的排队(散列?)活动列表。所以,当一个活动被创建时,添加到列表中("onactivitycreated")。然后,它最终会被写入("onactivitywritten"),比较和删除成功的可能(不知道摩卡有什么意义)。当你的测试完成后,你可以查看列表是否为空。

      例如,您可以使用util.inherits(Activity, EventEmitter) 来扩展具有事件功能的 Activity 类。

      现在,您仍然需要在列表中等待/超时,如果有未通过事件处理的故障,您也需要处理。

      编辑 -- 忽略下面的建议,因为它是一个有趣的异步演示,对你不起作用。 :)

      如果您想测试它们,我会看看像 async 这样的库,您可以在其中执行一系列代码(或在本例中为 waterfall),这样您就可以首先创建一个User,然后在完成后验证是否已记录正确的 Activity。我在这里使用了waterfall,以便可以将值从一个任务传递到下一个任务。

      async.waterfall([
          function(done) {
             User.create({name: "foo"}, function (err, user) {
                 if (err) { done(err); return; }
                 done(null, user._id);  // 2nd param sent to next task as 1st param
             });
      
          },
          function(id, done) {  // got the _id from above
              // substitute efficient method for finding
              // the corresponding activity document (maybe it's another field?)
              Activity.findById(id, function (err, act) {
                  if (err) { done(err); return; }
                  if (act) { done(null, true);
                  done(null, false); // not found?!
              });
          }
      ], function(err, result) {
         console.log("Success? " + result);
      });
      

      【讨论】:

      • waterfall 会遇到同样的问题,因为即使User.create 完成(并调用done),中间件操作也可能无法完成。所以我猜答案是使用setTimeout :/
      • 你是说对 Create 的调用完全失败(根本不返回也不调用回调)?
      • 不,create 工作正常,但在您的示例中 done 将在 Activity.create 完成之前被调用,因此 .find 将找不到任何东西。
      • 啊。明白了。使用事件提供的替代建议。 setTimeout 似乎很脆弱,所以我想要避免它的东西。 :)
      • 您的建议很有趣,但我无法访问 Activity 实例来使用 .on。有什么方法可以使事件在类上全局可访问或静态可访问?
      猜你喜欢
      • 2016-04-24
      • 1970-01-01
      • 2021-12-29
      • 2019-11-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-18
      • 1970-01-01
      • 2011-11-03
      相关资源
      最近更新 更多