Bluebird 承诺在使用 Sinon 的假计时器时冻结

Bluebird promises freeze when using Sinon's fake timer

以下测试在与 Sinon 的假计时器和 Bluebird 一起使用时冻结。

var sinon = require('sinon');
var Promise = require('bluebird');

describe('failing test', function() {
  beforeEach(function() {
    this.clock = sinon.useFakeTimers();
  });
  afterEach(function() {
    this.clock.restore();
  });
  it('test', function(done) {
    Promise.delay(1000).then(function(){
        done(); //This never gets called     
    });
  });
});

我正在使用 Mocha (v2.2.5) 与 Bluebird (v2.9.33) 和 Sinon (v1.15.3)。

我尝试了 Bluebird 和 Sinon 的一些讨论中提供的建议,但我无法完成这项工作。这似乎是 Sinon 存根 setImmediate 方式的问题,但除此之外我不知道如何解决这个问题。

您需要像这样手动设置假定时器:

describe('failing test', function() {
  it('test', function(done) {
    Promise.delay(1000).then(function(){
        done(); //This never gets called     
    });
    //
    // ADVANCE THE CLOCK:
    //
    this.clock.tick(1000);
  });
});

顺便说一句,mocha 内置了对 promise 的支持,所以更好的方法是 return 你的 promise:

describe('failing test', function() {
  it('test', function() { // No more "done" callback
    var p = Promise.delay(1000).then(function(){
        console.log('done'); 
    });
    this.clock.tick(1000);
    return p; // Return the promise instead - mocha will take of the async magic goodness!
  });
});

根据我的经验,混合使用 promises 和 done 回调风格会导致各种麻烦和难以跟踪的错误。使用 promises 时,尽量坚持返回,并查看 chai-as-promised 等库。我向你保证,它会让你的测试更具可读性!

最佳实践:

不要使用假计时器,它们会导致奇怪的并发问题,因为它们会同步调用延迟函数 - 从而改变执行。而是使用 Mocha 的内置承诺支持:

describe('failing test', function() {
    it('test', function(){ 
        return Promise.delay(1000); // return the promise here, no `done`
    });

但如果你必须

请不要这样做,但您可以告诉 bluebird 运行 它同步回调,我强烈建议不要这样做,它 导致您的应用出现计时问题:

Promise.setScheduler(function(fn){
    return fn();
});