You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Jest测试定时器超时后触发的React组件重新渲染

问题根因

jest.useFakeTimers()仅模拟了setTimeout等定时器类宏任务,你用到的delay是返回Promise的异步封装:定时器回调被jest.runAllTimers()触发后,Promise的resolve回调属于微任务队列,不会被同步执行,此时await delay(1000)后的setFoo逻辑还未运行,就触发了后续断言,自然会出现断言失败、以及React状态更新未被act包裹的警告。

修复方案

在jest.runAllTimers()执行后,新增一行等待微任务队列清空的逻辑即可,你的测试用例已经声明为async函数,直接加await Promise.resolve()就能让所有 pending 状态的微任务执行完毕,等待React完成重渲染后再执行断言。

修改后的完整测试代码:

import { render } from '@testing-library/react';
import { useEffect, useState } from 'react';
import { delay } from './delay';
describe('useState', () => {
  it("should set rerender when setting state", async () => {
    jest.useFakeTimers();
    let renderCount = 0;
    function MyComponent() {
      const [foo, setFoo] = useState("bar");
      ++renderCount;
      useEffect(() => {
        (async function asyncEffect() {
          await delay(1000);
          setFoo("mew")
        })()
      }, [])
      return (<div data-testid="test">{foo}</div>);
    }

    const { getByTestId } = render(<MyComponent />)
    expect(getByTestId("test").textContent).toEqual("bar");
    expect(renderCount).toEqual(1);
    jest.runAllTimers();
    // 新增等待微任务执行完毕的逻辑
    await Promise.resolve();
    expect(getByTestId("test").textContent).toEqual("mew");
    expect(renderCount).toEqual(2);
  })

  afterEach(() => {
    jest.useRealTimers();
  })
})

可选优化

你也可以直接用Testing Library提供的异步查询能力简化代码,把断言改为expect(await findByTestId('test')).toHaveTextContent('mew'),该方法会自动等待异步更新完成,不需要手动处理定时器和微任务的时序问题。


内容的提问来源于stack exchange,提问作者Archimedes Trajano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 16:57:02