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

Jest测试引入IconButton组件时触发document全局变量异常问题

Jest测试含Material-UI IconButton的React组件时触发document未定义错误的原因与解决

错误信息

Error: The `document` global was defined when React was initialized, but is not defined anymore. This can happen in a test environment if a component schedules an update from an asynchronous callback, but the test has already finished running. To solve this, you can either unmount the component at the end of your test (and ensure that any asynchronous operations get canceled in `componentWillUnmount`), or you can change the test itself to be asynchronous.
    at Object.invokeGuardedCallbackDev (C:\Users\512619\source\repos\webpage\WebPage\client\node_modules\react-test-renderer\cjs\react-test-renderer.development.js:12556:15)
    ...(省略其余调用栈信息)

问题场景

  • 测试LogContainer组件时,仅当组件包含@mui/material的<IconButton/>时触发上述错误,移除该组件则测试正常,同库的<Typography/>可正常使用
  • 错误触发后,后续测试会永久挂起
  • 已尝试方案:将测试设为异步、指定/** @jest-environment jsdom */、使用setImmediate,均未解决问题

原因分析

核心原因是IconButton内部存在未被正确处理的异步操作,在测试结束后才触发组件更新,此时Jest已清理jsdom环境(包括document全局对象),具体细节:

  • IconButton基于Material-UI的ButtonBase实现,内部包含焦点状态异步处理、requestAnimationFrame回调或防抖/节流逻辑,这些操作的执行时机晚于测试结束时间
  • Jest默认在测试完成后立即销毁jsdom环境,当异步回调触发React状态更新时,document已不存在,导致React抛出该错误
  • 这些异步操作未在组件卸载时被取消,即使测试结束,回调仍会执行,进而污染后续测试环境,导致测试挂起

解决方案

1. 确保组件卸载时清理异步操作

  • 若LogContainer是类组件,在componentWillUnmount中取消所有定时器、requestAnimationFrame、防抖/节流函数;若是函数组件,使用useEffect的清理函数:
    useEffect(() => {
      const rafId = requestAnimationFrame(() => {});
      return () => {
        cancelAnimationFrame(rafId);
        // 清理其他异步操作
      };
    }, []);
    
  • 若异步逻辑来自IconButton内部,可在测试中mock相关API为同步执行:
    jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => {
      cb();
      return 0;
    });
    

2. 显式等待异步操作完成后结束测试

使用@testing-library/react的waitFor等待组件状态稳定,再卸载组件:

import { render, waitFor } from '@testing-library/react';

test('renders LogContainer with IconButton', async () => {
  const { unmount } = render(<LogContainer />);
  // 等待IconButton的异步操作完成,可根据组件状态判断,比如等待焦点状态处理完毕
  await waitFor(() => expect(document.querySelector('.MuiIconButton-root')).toBeInTheDocument());
  unmount();
});

3. 调整Jest配置延迟环境清理

在Jest配置文件(如jest.config.js)中添加testEnvironmentOptions,给异步操作留足够执行时间:

module.exports = {
  testEnvironment: 'jsdom',
  testEnvironmentOptions: {
    teardownTimeout: 1000 // 延迟1秒清理环境
  }
};

4. 替换测试工具为React Testing Library

放弃使用react-test-renderer,改用React Testing Library,它对异步场景的处理更友好,能自动等待组件完成更新,减少此类环境冲突问题。

内容的提问来源于stack exchange,提问作者Arthur Attout

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 22:53:16