预期调用一个断言但收到零个断言调用。为什么?

Expected one assertion to be called but received zero assertion calls. Why?

我正在编写一个 rejectOnTimeout() 函数,如果承诺未在 ms 中完成,该函数应该 return 具有 timeout_error 值的被拒绝的承诺,或者 return 反映原始承诺行为的承诺。

我认为我得到的答案是正确的。但是我不明白错误的意思 Expected one assertion to be called but received zero assertion calls.

在这种情况下我做错了什么?

const delay = require('delay');

/**
 * @param  {Promise} original promise 
 * @param  {Number}  ms for timeout
 * @return {Promise}
 */
const rejectOnTimeout = (promise, ms) => new Promise((resolve, reject) => {
  const start = Date.now();

  promise.finally(() => {
    const time = Date.now() - start;

    if (time <= ms) {
      resolve(promise);
    }
    reject('timeout_error');
  });
});

// Test 1 [PASSED]
rejectOnTimeout(Promise.resolve(10), 100)
  .then(data => console.log(data)) // 10
  .catch(err => console.error(err));

// Test 2 [PASSED]
rejectOnTimeout(Promise.reject(10), 100)
  .then(data => console.log(data))
  .catch(err => console.error(err)); // 10

// Test 3 [FAILED], REASON: Expected one assertion to be called but received zero assertion calls.
const delayed = delay(100, { value: 10 });

rejectOnTimeout(delayed, 50)
  .then(data => console.log(data))
  .catch(err => console.error(err)); // timeout_error

测试代码:

describe('rejectOnTimeout', () => {
    it('rejectOnTimeout, 01', async() => {
      expect.assertions(1);
      await expect(rejectOnTimeout(Promise.resolve(10), 100)).resolves.toBe(10);
    });

    it('rejectOnTimeout, 02', async() => {
      expect.assertions(1);
      await expect(rejectOnTimeout(Promise.reject(10), 100)).rejects.toBe(10);
    });

    it('rejectOnTimeout, 03', async() => {
      expect.assertions(1);
      const delayed = delay(100, { value: 10 });

      await expect(rejectOnTimeout(delayed, 50)).rejects.toBe('timeout_error');
    });

    it('rejectOnTimeout, 04', async() => {
      expect.assertions(1);
      const delayed = delay.reject(100, { value: 10 });

      await expect(rejectOnTimeout(delayed, 50)).rejects.toBe('timeout_error');
    });

    it('rejectOnTimeout, 05', async() => {
      expect.assertions(1);
      const delayed = delay(100, { value: 10 });

      await expect(rejectOnTimeout(delayed, 1000)).resolves.toBe(10);
    });

    it('rejectOnTimeout, 06', async() => {
      expect.assertions(1);
      const delayed = delay.reject(100, { value: 'error' });

      await expect(rejectOnTimeout(delayed, 1000)).rejects.toBe('error');
    });

当我将您的代码插入我的编辑器时,我遇到了不同的错误:

● rejectOnTimeout › rejectOnTimeout, 03

thrown: 10

问题是之前的测试将未处理的拒绝扔到测试环境中,一切都爆炸了。将 rejectOnTimeout 实现更改为此后,所有测试都通过了:

const rejectOnTimeout = (promise, ms) =>
  new Promise((resolve, reject) => {
    const start = Date.now();

    promise
      .catch((e) => reject(e))
      .finally(() => {
        const time = Date.now() - start;

        if (time <= ms) {
          resolve(promise);
        }
        reject("timeout_error");
      });
  });

Promise.finally 并没有实际处理拒绝,它只是触发 Promise 是拒绝还是解决。因此,虽然我们在 rejectOnTimeout 中的新 Promise 可能正确完成,但未处理底层的 promise 参数。

内部捕获似乎可以保留您正在寻找的行为。如果 promise 已经抛出错误,我们大概并不关心是否有超时。 finally 中发生的事情也无关紧要,因为您无法在已经拒绝后覆盖 Promise 的结果。

以下是我将如何实现此功能:

const rejectOnTimeout = (promise, ms) => {
   return new Promise((resolve, reject) => {
     setTimeout(() => reject("timeout_error"), ms);
     
     promise
       .catch((e) => reject(e))
       .finally(() => resolve(promise));
   });
}