测试承诺直到暂停辛农柴才得到解决

2022-06-11 00:00:00 javascript sinon promise chai

我们有一个简单的等待方法,它利用节点应用程序中的承诺

exports.wait = (timeout) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve()
    }, timeout)
  });
};

我们尝试使用sinon和chai测试此行为。

我们设法使用Chai-as-Promised获得了正确的断言,但它只检查承诺解析,而不能让我们测试真实的行为:

  • 将100毫秒的值传递给等待方法时
  • 我们预计承诺不会在99ms内解析
  • 我们预计承诺在100毫秒内解析

承诺和计时器的结合确实让我们头疼。

这是我们的最后一次尝试设置:

const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
chai.use(require('sinon-chai'));
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);

const wait = require('./wait').wait;

var clock;

before(() => {
  clock = sinon.useFakeTimers();
});

after(() => {
  clock.restore();
})

it('should not resolve until given time', (done) => {
  const promise = wait(100);
  let fulfilled = false;

  promise.then(() => {
    fulfilled = true;
    done();
  });

  clock.tick(99);
  expect(fulfilled).to.be.false;
  clock.tick(2);
  expect(fulfilled).to.be.true;
});

fulfilled永远不会翻转为True,或者至少我们无法阅读它。

AssertionError:False应为True

那么如何在chai-sinon下将计时器与承诺测试混合在一起,以正确评估我们的定时解决方案?


解决方案

您可以从如下问题测试代码:

const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');

const wait = require('./wait').wait;

var clock;

before(() => {
  clock = sinon.useFakeTimers();
});

after(() => {
  clock.restore();
})

it('should not resolve until given time', async () => {  // <= async
  const promise = wait(100);
  let fulfilled = false;

  promise.then(() => {
    fulfilled = true;
    done();
  });

  clock.tick(99);
  await Promise.resolve();  // let any pending Promise callbacks run
  expect(fulfilled).to.be.false;  // Success!
  clock.tick(2);
  await Promise.resolve();  // let any pending Promise callbacks run
  expect(fulfilled).to.be.true;  // Success!
});

详细信息

Fake timers将setTimeout计划的回调转换为同步调用。

Promise回调在Promise解析时在PromiseJobs queue中排队,直到当前执行的消息完成后才运行。

在这种情况下,当前运行的消息是the test,因此将fulfilled设置为truethen回调直到测试完成后才会运行。

您可以使用async测试函数,并在要暂停当前运行的消息并允许运行任何排队的Promise回调的任何位置调用await Promise.resolve();

有关将假计时器与Promises一起使用的其他详细信息,请参阅this answer,其中使用Jest,但概念是相同的。

相关文章