如何使用假计时器测试递归的异步 JavaScript 函数?

How do I test a recursive, asynchronous JavaScript function using fake timers?

我有一个基本的递归函数,它在执行时可以正常工作。我想使用 Sinon 的假计时器来测试每个执行阶段的功能。

但是,假计时器似乎只适用于递归函数的第一次调用。

我很好奇是否有人可以帮助弄清楚如何让假定时器一直工作。

示例:

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms))
}

async function ensureCount(count, { attempt, delay }) {
  attempt = attempt || 0
  console.log('Attempt', attempt)

  if (attempt === count) {
    return
  }

  await wait(delay)
  await ensureCount(count, { attempt: attempt + 1, delay })
}

像这样测试(在this article的帮助下):

it('retries after a given delay', function() {
  const clock = sinon.useFakeTimers()
  const promise = ensureCount(2, { delay: 200 })

  clock.tick(200)
  // Make some assertions.

  clock.tick(200)
  // Make some assertions.

  clock.tick(200)
  // Make some assertions.

  return promise
})

预期的控制台输出(这是没有假计时器时发生的情况):

Attempt 0
Attempt 1
Attempt 2
✔ retries after a given delay (405ms)

实际的控制台输出(发生在假计时器上):

Attempt 0
Attempt 1
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.

问题:

有没有办法让假计时器应用于递归调用的每一步,而不仅仅是第一步?

原来Sinon为此提供了一个我没见过的.tickAsync()函数。 (感谢 )。

此代码解决了问题:

it('retries after a given delay', async () => {
  const clock = sinon.useFakeTimers()
  ensureCount(2, { delay: 200 })

  await clock.tickAsync(200)
  // Make some assertions.

  await clock.tickAsync(200)
  // Make some assertions.

  await clock.tickAsync(200)
  // Make some assertions.

  clock.restore()
})