如何用sinon和chai兑现承诺

时间:2019-05-23 01:26:43

标签: javascript node.js chai sinon

我们的CLI中有一个方法,该方法使用该方法向用户返回打印消息的承诺。

exports.handler = (argv) => {
  let customUtils = new Utils(argv);

  Utils.deploy()
    .then(res => console.log(`Ressource was deployed`))
    .catch(e => {
      console.error(`Ressource was not deployed`);
      console.error(e);
      process.exit(1);
    });
}

我们正在寻找一种方法来测试控制台错误并在deploy()承诺被拒绝的情况下退出进程。

我们尝试使用沙盒存根,然后在异步测试中声明:

describe('when promise is errored', () => {
  beforeEach(() => {
    sandbox = sinon.createSandbox();
    utilsStub = sandbox.stub(Utils.prototype, 'deploy').rejects('rejected');
    processStub = sandbox.stub(process, 'exit');
    consoleStub = sandbox.stub(console, 'error');
  });

  afterEach(() => {
    sandbox.restore();
  });

  it('should call deploy and log the error before exiting', async () => {
    await handler({});

    expect(utilsStub).to.have.been.called;
    expect(console.error).to.have.been.called;
  });
});

此测试无效:AssertionError: expected error to have been called at least once, but it was never called

当我们expect(process.exit).to.have.been.called;时也会发生同样的情况。从来没有叫过。

我们以相似的方式成功测试了then部分:

describe('when promise is resolved', () => {
  beforeEach(() => {
    sandbox = sinon.createSandbox();
    utilsStub = sandbox.stub(Utils.prototype, 'deploy').callsFake(() => Promise.resolve('some text'));
    consoleStub = sandbox.stub(console, 'log');
  });

  afterEach(() => {
    sandbox.restore();
  });

  it('should call deploy and print success message', async () => {
    await handler({});

    expect(utilsStub).to.have.been.called;
    expect(console.log).to.have.been.calledWith('Ressource was deployed');
  });
});

2 个答案:

答案 0 :(得分:1)

有些事情可以修复源文件和测试文件。

对于源文件,我们必须使用customUtils来调用deploy()函数。由于可以使用async/await,因此从Promise进行转换可以产生更好的代码。

exports.handler = async argv => { // put async
  let customUtils = new Utils(argv);
  try {
    await customUtils.deploy(); // change to await and use customUtils
    console.log(`Ressource was deployed`);
  } catch (e) {
    console.error(`Ressource was not deployed`);
    console.error(e); 
    process.exit(1);
  }
};

对于测试文件,没有任何变化

describe('when promise is errored', () => {
  beforeEach(() => {
    sandbox = sinon.createSandbox();
    utilsStub = sandbox.stub(Utils.prototype, 'deploy').rejects('rejected');
    processStub = sandbox.stub(process, 'exit');
    consoleStub = sandbox.stub(console, 'error');
  });

  afterEach(() => {
    sandbox.restore();
  });

  it('should call deploy and log the error before exiting', async () => {
    await handler({});

    expect(utilsStub).to.have.been.called;
    expect(console.error).to.have.been.called;
    expect(process.exit).to.have.been.called; // add it
  });
});

已更新:

如果要仍然使用Promise,我们必须确保我们返回了Promise。

exports.handler = (argv) => {
  let customUtils = new Utils(argv);

  return customUtils.deploy() // <== specify return here
    .then(res => console.log(`Ressource was deployed`))
    .catch(e => {
      console.error(`Ressource was not deployed`);
      console.error(e);
      process.exit(1);
    });
};

希望有帮助

答案 1 :(得分:1)

在测试您的断言之前,您需要await的结果exports.handler。您正在 等待中,但是exports.handler没有返回承诺,因此测试中没有等待的地方-exports.handler立即返回undefined,因此测试以相同的方式运行断言console.error之前的事件循环。

我不确定为什么在诺言解决的测试中您没有看到类似的问题。 (也许值得检查该测试是否正确失败)

这应该有帮助:

exports.handler = (argv) => {
  let customUtils = new Utils(argv);

  //Utils.deploy() // <- is that a typo?

    return customUtils.deploy()
      .then(res => console.log(`Ressource was deployed`))
      .catch(e => {
          console.error(`Ressource was not deployed`);
          console.error(e);
          process.exit(1);
       });
}

在测试中,您还将使用以下方式创建间谍:

consoleStub = sandbox.stub(console, 'error');

但是直接在console.error上写断言。我认为这不起作用:

expect(console.error).to.have.been.called;
// maybe expect(consoleStub)...

有了这些更改,测试对我来说就通过了,(更重要的是)当我没有在console.error中调用catch时,测试失败了。