Sinon,使用 Promise.reject() 到 stub.rejects() 观察不同的行为

Sinon, observing different behaviour using Promise.reject() to stub.rejects()

我遇到了问题 运行 测试(在 Node 中),

我正在模拟一个被拒绝的承诺,我的代码应该重试(如果可能相关的话,使用 promise-retry)。

当我使用 stub.returns(Promise.reject(error)

模拟被拒绝的承诺时

我收到未捕获的错误警告(对于我的 dummyErrors),即使我在调用我的函数时捕获错误...

-请注意,这些未捕获的错误仅发生在单元测试中,而不是实际调用中。

const mockedFunction = sinon.stub();

const dummyError = new Error('Document is locked');

mockedFunction.onCall(0).returns(Promise.reject(dummyError));
mockedFunction.onCall(0).returns(Promise.reject(dummyError));
mockedFunction.onCall(0).returns(Promise.reject(dummyError));
mockedFunction.onCall(1).returns(Promise.resolve({approved: true}));

我发现通过更改为使用 stub.rejects() 语法:

mockedFunction.onCall(0).rejects(dummyError);
mockedFunction.onCall(1).rejects(dummyError);
mockedFunction.onCall(2).rejects(dummyError));
mockedFunction.onCall(3).resolves({approved: true});

我不再收到未捕获的错误警告。

我的问题已解决,但我想更好地了解原因,我查看了 sinon 源代码,看起来 .rejects 的实现没有什么不同

在本质上检测未捕获错误的 promise 实现中(包括 V8/Node.js),来自被拒绝的 promise 的错误应该在同一时刻被捕获,否则它会触发 UnhandledPromiseRejectionWarning.

这会很好用:

let promise = Promise.reject();
promise.catch(() => {});

这将导致潜在的未处理承诺并触发警告:

let promise = Promise.reject();
setTimeout(() => {
  promise.catch(() => {});
});

如果 Promise.reject(dummyError) 没有与 catch(...)then(..., ...) 链接在同一个报价单上,它会触发警告,而如果 rejects(dummyError) 则创建一个被拒绝的承诺在函数调用中,所以这可能是真的:

sinon.spy(Promise, 'reject');
mockedFunction.onCall(0).rejects(dummyError);
expect(Promise.reject).to.have.not.been.called;
mockedFunction();
expect(Promise.reject).to.have.been.called;

rejects(dummyError) 的替代方法是:

mockedFunction.onCall(0).callsFake(() => Promise.reject(dummyError))