开玩笑:测试超时后拒绝的承诺

Posted

技术标签:

【中文标题】开玩笑:测试超时后拒绝的承诺【英文标题】: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_STREAMms`)
  

我设法编写了一个通过的测试,但它需要 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 中。你如何使用假计时器测试这样的功能?

【问题讨论】:

【参考方案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 秒。

以上是关于开玩笑:测试超时后拒绝的承诺的主要内容,如果未能解决你的问题,请参考以下文章

排毒自动化测试随机超时

使用开玩笑超时错误进行排毒:“分配给未定义”

测试有超时的 axios api 调用

错误:超过 2000 毫秒的超时。带有承诺的单元测试

嵌套承诺 - 摩卡 - 超过超时

使用 supertest 在快速服务器上开玩笑超时