【问题标题】:Test async.waterfall in node.js with sinon.js timers使用 sinon.js 计时器在 node.js 中测试 async.waterfall
【发布时间】:2016-09-16 16:22:09
【问题描述】:

我希望测试以下函数,其中大量使用了 async.js:

MyClass.prototype.pipeline = function(arg1, arg2) {
    ...
    async.waterfall([
        async.apply(self.a.f.bind(self.a), arg1, arg2),
        function(data, callback) {
            async.each(data, function(d, callback) {
                async.waterfall([
                    async.apply(self.b.f.bind(self.b), d),
                    self.c.f.bind(self.c),
                    self.d.f.bind(self.d),
                    self.e.f.bind(self.f)
                ], function(err, results) {
                    if (err) {
                        ...
                    }
                    callback();
                });
            }, function(err) {
                callback(err, data);
            });
        }
    ], function (err, result) {
        ...
    });
};

现在我知道我可以将这里发生的很多事情分开来分开函数,但它是一个流线型操作的管道,在前一个完成后彼此传递数据,所以我希望保持这样的状态而不是示例将函数 function(data, callback) {...} 与名称类似 BCDEpipeline 分开。 无论如何,我的问题是我基于第一个async.waterfall() done 函数的 done 回调做了一些断言,问题是它被稍后调用(延迟)即使我已经存根了 a、b、c、d 和 e 函数并且使他们立即产生下一个回调。请注意,我不能只存根 async.waterfall() 并使其产生完成回调,因为我将留下未经测试的代码的关键分支(async.each() 和第二个 async.waterfall() 的内部完成回调。 在像这样调用MyClass.prototype.pipeline() 函数后,我尝试使用 sinon 假计时器并使用this.clock.tick(0);

var obj = new MyClass();
obj.pipeline(5, 3);
this.clock.tick(0);
/* assertions */
...

但即便如此,断言部分还是在调用任何完成函数之前执行的。我试图深入研究异步库代码,看看它是如何调用它的完成函数的仍然是先执行。 如果我使用一些嵌套的 setImmediate() 调用它工作正常,但这是这个问题最糟糕的解决方案。

【问题讨论】:

  • .bind 的全部用途是什么?这是您的实际代码还是您正在为这个问题缩短一些内容?
  • @clay 我使用 bind 来调用使上下文成为实例本身,这是从不相关部分中剥离出来的代码,你看到的任何地方... 都是不相关的部分。所有的名字都是虚构的(a、b、c、d、e、f、arg1、arg2 等)

标签: javascript node.js unit-testing sinon async.js


【解决方案1】:

你遗漏了很多东西,所以很难说这应该做什么。老实说,虽然这不是你的错,但对我来说这看起来像是噪音。

如果您实际上不需要并行执行大量 IO,那么这就是我的建议。切换到 ES2017 和 babel。使用带有@autobind 的类或如下语法:

class Test {
  constructor(testB) {
    this.testB = testB;
  }

  fuzz = async x => x+1;

  async foo = (bar) => {
    try {
      const result = await this.testB.blah(bar);
      return await this.fuzz(result);
    } catch (e) {
      console.error(e);
      return null;
    }
  }

  async fizz = (bars) => await Promise.all(bars.map(this.foo));
}

然后使用ava 进行测试。这样,您就有了一种干净的方式 (await) 来处理具有循环和异步测试的异步函数的基本流程,并且您的方法的 this 将始终指向您的实例。

【讨论】:

  • 不幸的是我使用了很多 I/O,现在切换到 babel 工作量太大,我现在负担不起
  • 好的,澄清一下我的意思是并行 I/O,因为您似乎是在尝试一次执行一个 I/O,而不是同时执行一堆。
  • 哦,我看到你有一个 async.each ,这是并行部分。我会在 Promise.all 中使用 try catch 使其成为一个单独的函数。无论如何,也许你可以在有时间做 babel 等时进行调查。祝你好运。
【解决方案2】:

好的,根据我猜的以下问题,这是旧版本中的一个错误: https://github.com/caolan/async/issues/609
https://github.com/caolan/async/issues/106

虽然它们已经很旧并且我使用了更新的版本,但我刚刚更新了我的依赖项,它现在可以正常工作了。

【讨论】:

    猜你喜欢
    • 2016-08-16
    • 1970-01-01
    • 1970-01-01
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-26
    • 1970-01-01
    相关资源
    最近更新 更多