【问题标题】:Jest: Test for promise that rejects after timeout开玩笑:测试超时后拒绝的承诺
【发布时间】:2021-01-10 13:01:53
【问题描述】:

我正在尝试为这个函数的悲惨路径写一个测试:

const awaitFirstStreamForPage = async page => {
  try {
    await page.waitForSelector('[data-stream="true"]', {
      timeout: MAX_DELAY_UNTIL_FIRST_STREAM,
    })
  } catch (e) {
    throw new Error(`no stream found for ${MAX_DELAY_UNTIL_FIRST_STREAM}ms`)
  }
}

我设法编写了一个通过的测试,但它需要 10 秒才能运行,因为它实际上是在等待测试完成。

describe('awaitFirstStreamForPage()', () => {
  it('given a page and no active stream appearing: should throw', async () => {
    jest.setTimeout(15000)

    const browser = await puppeteer.launch({ headless: true })
    const page = await getPage(browser)

    let error

    try {
      await awaitFirstStreamForPage(page)
    } catch (err) {
      error = err
    }

    const actual = error.message
    const expected = 'no stream found for 10000ms'

    expect(actual).toEqual(expected)

    await browser.close()
    jest.setTimeout(5000)
  })
})

可能有一种方法可以使用 Jest 的假计时器来解决它,但我无法让它工作。这是我最好的尝试:

const flushPromises = () => new Promise(res => process.nextTick(res))

describe('awaitFirstStreamForPage()', () => {
  it('given a page and no active stream appearing: should throw', async () => {
    jest.useFakeTimers()

    const browser = await puppeteer.launch({ headless: true })
    const page = await getPage(browser)

    let error

    try {
      awaitFirstStreamForPage(page)
      jest.advanceTimersByTime(10000)
      await flushPromises()
    } catch (err) {
      error = err
    }

    const actual = error.message
    const expected = 'no stream found for 10000ms'

    expect(actual).toEqual(expected)

    await browser.close()
    jest.useRealTimers()
  })
})

失败并抛出

(node:9697) UnhandledPromiseRejectionWarning: Error: no stream found for 10000ms

即使我将失败的函数包装在 try/catch 中。你如何使用假计时器测试这样的功能?

【问题讨论】:

    标签: javascript unit-testing timer jestjs settimeout


    【解决方案1】:

    如果没有等待,则不可能通过try..catch 捕获来自awaitFirstStreamForPage(page) 的拒绝。

    应该在调用advanceTimersByTime 并且可能在flushPromises 之后捕获拒绝。

    可以是:

    const promise = awaitFirstStreamForPage(page);
    promise.catch(() => { /* suppress UnhandledPromiseRejectionWarning */ });
    
    jest.advanceTimersByTime(10000)
    await flushPromises();
    await expect(promise).rejects.toThrow('no stream found for 10000ms');
    

    【讨论】:

      【解决方案2】:

      问题似乎不在于使用假计时器:您预期的错误是被抛出的错误。但是,当测试在 Jest 中抛出错误的函数时,您应该将抛出错误的代码包装在一个函数中,如下所示:

      expect(()=> {/* code that will throw error */}).toThrow()

      更多详情:https://jestjs.io/docs/en/expect#tothrowerror

      编辑:对于异步函数,您应该在toThrow 之前使用rejects;看这个例子:Can you write async tests that expect toThrow?

      【讨论】:

      • toThrow 捕获同步错误。应该是rejects.toThrow。这还不够,因为操作(advanceTimersByTime)需要同时执行。这里使用了假定时器,使测试时间短于 10 秒。
      猜你喜欢
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 2019-09-15
      • 2018-12-10
      • 1970-01-01
      • 2019-07-21
      • 2021-03-16
      • 2018-07-02
      相关资源
      最近更新 更多