如何将用户事件库与Jest的伪定时器结合使用?

fsi0uk1n  于 2022-12-08  发布在  Jest
关注(0)|答案(2)|浏览(161)

我正在为一个React组件编写Jest测试,并使用@testing-library/user-event库来模拟用户交互。
下面是一个示例测试:

it(`fires onClick prop function when the button is clicked`, async () => {
  // jest.useFakeTimers()

  let propFn = jest.fn()

  let app = RTL.render(
    <SampleComp onClick={propFn} />
  )

  await userEvent.click(app.queryByText('Test button')!)

  expect(propFn).toHaveBeenCalled()

  // jest.useRealTimers()
})

这是组件:

function SampleComp({ onClick }) {
  // a simple bridge for debugging
  function _onClick(e) {
    console.log(`_onClick invoked`)
    return onClick(e)
  }

  return (
    <button onClick={_onClick}>
      Test button
    </button>
  )
}

如果没有假计时器,测试会在几分之一秒内运行并通过。如果有假计时器,测试会超时并失败:

● fires onClick prop function when the button is clicked

    thrown: "Exceeded timeout of 5000 ms for a test.
    Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."

它也无法发出调试行,因此显然不会触发click事件。
我已经做了大量的调试,并确定根本原因是用户事件库在内部依赖于setTimeout来创建事件之间的短暂延迟。
是的,"事件"* 复数 *--回想一下用户事件库的价值主张是:
fireEvent调度DOM事件,而user-event模拟完整的 * 交互 *,这可能会触发多个事件并在过程中执行其他检查。

  • -用户事件文档
    在本例中,user-event为我创建了两个事件:
    1.鼠标移动1
    1.实际点击
    1尽管user-event的内部事件对象中的第一个事件只有一个target而没有type,但我认为第一个事件应该是鼠标移动或鼠标悬停在按钮上。我已经验证了用户事件在内部创建的操作列表确实有2个项目,第二个项目明确标记为"[MouseLeft]"。
    延迟是由user-event的pointerAction函数插入到事件之间的。在我的例子中,user-event在事件1和2之间的延迟期间停止。
    超时在user-event的wait模块中创建:
new Promise<void>(resolve => globalThis.setTimeout(() => resolve(), delay))

我已经通过monkeypatch我的node_modules中的那一行来验证了这是一个问题,这样就可以在没有计时器的情况下立即解决问题,如下所示:

new Promise<void>(resolve => resolve())

不幸的是,我的应用程序代码有一些重要的计时器,让测试套件实时等待它们是不可行的。即使我可以,我怀疑那些后来发现这个问题的人可能没有这样的自由。
运行Jest的假计时器以允许用户事件继续进行是不起作用的,如下所示:

it(`fires onClick prop function when the button is clicked`, async () => {
  jest.useFakeTimers()

  let propFn = jest.fn()

  let app = RTL.render(
    <SampleComp onClick={propFn} />
  )

  let userAction = userEvent.click(app.queryByText('Test button')!)

  jest.runOnlyPendingTimers() // no good
  jest.runAllTimers() // also no good

  await userAction

  expect(propFn).toHaveBeenCalled()

  jest.useRealTimers()
})

那么,如何将Jest的假计时器与用户事件库结合使用呢?
在这种情况下,版本似乎确实很重要:这个测试在早期版本的React、Jest和user-event上运行得很好。下面是我现在使用的版本(在这里中断),以及我正在使用的版本(在这里正常工作):
| 图书馆|电流(休息)|上一个(已完成)|
| - -| - -| - -|
| 开玩笑|v27.5.1|v26.6.3|
| React了|v18.1.0|v16.13.1|
| @测试库/用户事件|v14.3.0|v12.8.3|
我需要注意的是,它是不可能改变正在使用的软件包版本。我需要一个解决方案,工作与特定版本中列出的"当前"列。
注意:我认为这个问题并不是React特有的,使用Vue或Svelte或其他任何东西的人,只要他们使用Jest的假定时器+用户事件(两者都是框架不可知的),就会有同样的问题。

guicsvcw

guicsvcw1#

您可以使用user-event设置功能禁用延迟:

const user = userEvent.setup({ delay: null })

然后确保使用setup()返回的示例:

await user.click(app.queryByText('Test button')!)
mkh04yzy

mkh04yzy2#

您是否尝试过以下操作:

const ue = userEvent.setup({ advanceTimers: jest.advanceTimersByTime });

在我的情况下,如果没有设置它就不工作。更多这里Mocking setTimeout with Jest

相关问题