【问题标题】:how to assert catch promise with sinon and chai如何与 sinon 和 chai 兑现承诺
【发布时间】:2019-05-23 01:26:42
【问题描述】:

我们的 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() promise 被拒绝的情况下退出进程。

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

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');
  });
});

【问题讨论】:

    标签: javascript node.js chai sinon


    【解决方案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);
        });
    };
    

    希望对你有帮助

    【讨论】:

    • 它有效,但我真的不明白有什么不同。那么使用 then().catch() 应该有效吗?
    • @BlackHoleGalaxy 它不会因为我们没有指定承诺的返回声明。查看我的更新答案:)
    • 知道了。忘记在自动承诺返回中定义异步方法转换。
    【解决方案2】:

    在测试您的断言之前,您需要能够await exports.handler 的结果。你正在等待它,但是exports.handler 没有返回承诺,所以在测试中没有什么可以等待的——exports.handler 立即返回 undefined 所以测试在 @ 之前的同一个事件循环中运行断言可以拨打987654325@。

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

    这应该会有所帮助:

    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)...
    

    通过这些更改,测试通过了我,并且(更重要的是)当我不在catch 中调用console.error 时失败。

    【讨论】:

    • 针对 console.error 编写断言有效。例如,在更改成功消息时,我设法使第一个测试失败。
    猜你喜欢
    • 2016-04-29
    • 2016-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多