React 18测试懒加载组件时消除act()警告的方法
React 18 懒加载组件测试中Suspended警告的解决方案
问题描述
参考React官方文档编写App组件测试用例,用act()包裹渲染逻辑后测试可正常通过,但当App包含React懒加载组件时,出现「Suspended资源加载完成事件未被act()包裹」的警告。尝试过await waitFor()、findBy()、移除act()等方案,均未解决,求React 18环境下的可行方案。
相关代码与环境
测试代码
import ReactDOM from 'react-dom/client'; import { act } from 'react-dom/test-utils'; import App from './App' import { screen } from '@testing-library/react' let container: HTMLElement; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); it('can render and update a counter', () => { act(() => { ReactDOM.createRoot(container).render(<App />); }); const title = screen.getByText('My App'); expect(title).toBeInTheDocument() });
警告信息
Warning: A suspended resource finished loading inside a test, but the event was not wrapped in act(...). When testing, code that resolves suspended data should be wrapped into act(...): act(() => { /* finish loading suspended data */ }); /* assert on the output */ This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
setupTest.ts
import '@testing-library/jest-dom';
package.json依赖
"dependencies": { "@react-spring/web": "^9.4.5", "react": "^18.1.0", "react-dom": "^18.1.0", "styled-components": "^5.3.5" }, "devDependencies": { "@react-spring/types": "^9.4.5", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.2.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.1", "@types/react": "^18.0.9", "@types/react-dom": "^18.0.5", "@types/styled-components": "^5.1.25", "@typescript-eslint/eslint-plugin": "^5.36.1", "@typescript-eslint/parser": "^5.36.1", "eslint": "^8.23.0", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^3.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.31.1", "eslint-plugin-react-hooks": "^4.6.0", "prettier": "^2.7.1", "react-scripts": "5.0.1", "typescript": "^4.6.4" }
环境信息
Node版本:16.16
解决方案
方案1:使用Testing Library内置render函数(推荐)
React Testing Library在React 18下已自动适配act()逻辑,包括懒加载组件的Suspend状态处理,无需手动包裹act():
import { render, screen } from '@testing-library/react' import App from './App' it('can render and update a counter', async () => { render(<App />); // 懒加载组件需异步加载,用findByText(异步查询)替代同步的getByText const title = await screen.findByText('My App'); expect(title).toBeInTheDocument() });
方案2:异步包裹act()处理Suspend状态
若必须手动使用ReactDOM.createRoot,需将渲染逻辑改为异步,等待Suspend资源加载完成:
import ReactDOM from 'react-dom/client'; import { act } from 'react-dom/test-utils'; import App from './App' import { screen } from '@testing-library/react' let container: HTMLElement; beforeEach(() => { container = document.createElement('div'); document.body.appendChild(container); }); it('can render and update a counter', async () => { await act(async () => { ReactDOM.createRoot(container).render(<App />); // 等待Suspend资源加载完成的微任务执行 await new Promise(resolve => setTimeout(resolve, 0)); }); const title = screen.getByText('My App'); expect(title).toBeInTheDocument() });
方案3:Mock懒加载组件(单元测试场景)
若测试仅关注App组件本身,可Mock懒加载组件避免触发Suspend状态:
假设App中使用const LazyComponent = React.lazy(() => import('./LazyComponent')),测试中Mock如下:
import { render, screen } from '@testing-library/react' import App from './App' // Mock懒加载组件 jest.mock('./LazyComponent', () => ({ default: () => <div>Lazy Component Mock</div> })); it('can render and update a counter', () => { render(<App />); const title = screen.getByText('My App'); expect(title).toBeInTheDocument() });
关键原因说明
React 18中React.lazy加载组件会触发Suspend状态,手动同步act()无法覆盖异步加载完成后的状态更新。Testing Library的render和findBy*异步查询方法已适配React 18并发特性,能自动将所有UI变化包裹在act()中,解决警告问题。
内容的提问来源于stack exchange,提问作者JoaoPedroMatias
相关产品推荐
相关产品推荐

