Next.js Jest测试Image组件触发未包裹act(...)报错如何解决
Next.js 接入Jest测试时next/image触发act未包裹报错处理
问题现象
通过create-next-app创建的基础Next.js应用,接入Jest作为测试框架后,运行测试时触发报错:An update to Image inside a test was not wrapped in act(...)
初步排查该问题和Next.js内置next/image组件渲染完成后内部触发状态更新有关,涉及代码如下:
页面组件代码
import type { NextPage } from 'next' import Head from 'next/head' import Image from 'next/image' import styles from '../styles/Home.module.css' const Home: NextPage = () => { return ( <div className={styles.container}> <Head> <title>Create Next App</title> <meta name="description" content="Generated by create next app" /> <link rel="icon" href="/favicon.ico" /> </Head> <main className={styles.main}> Powered by{' '} <span className={styles.logo}> <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} /> </span> </main> </div> ) } export default Home
测试用例代码
import React from 'react' import { render, act } from '@testing-library/react' import { axe } from 'jest-axe' import Home from '@/pages/index' it('should demonstrate this matcher`s usage with react testing library', async () => { const { container } = render(<Home/>) const results = await axe(container) expect(results).toHaveNoViolations() })
报错堆栈定位到node_modules/next/client/image.tsx:353:3位置,查阅Next.js源码确认,该位置是Image组件内部调用的useLayoutEffect钩子,对应源码截图如下:
根因
next/image组件内部在useLayoutEffect中执行了状态更新逻辑,React测试库的render方法默认不会等待组件内部所有副作用执行完成,后续运行axe检测时状态更新仍在执行,就会触发act警告。
解决方案
- 方案1(官方推荐,最优解):测试环境Mock next/image组件
单元测试场景不需要验证next/image的图片优化、懒加载等内置逻辑,直接在Jest配置中添加mock,将next/image替换为原生img标签即可从根源避免该问题:
如果使用Next.js官方提供的next/jest配置,直接在测试setup文件中加入mock即可:
// jest.setup.js jest.mock('next/image', () => ({ __esModule: true, default: (props) => { // eslint-disable-next-line jsx-a11y/alt-text, @next/next/no-img-element return <img {...props} /> }, }))
如果是自定义Jest配置,可以先创建__mocks__/next/image.js文件:
const NextImage = ({ src, alt, width, height, ...props }) => { return <img src={src} alt={alt} width={width} height={height} {...props} /> } export default NextImage
再在jest.config.js中添加模块映射:
module.exports = { // 其他原有配置 moduleNameMapper: { '^next/image$': '<rootDir>/__mocks__/next/image.js', } }
- 方案2:等待所有副作用执行完成后再跑断言
如果不想mock组件,可以使用React测试库提供的waitFor工具,等待组件所有更新完成后再执行后续检测逻辑,修改测试用例如下:
import React from 'react' import { render, waitFor } from '@testing-library/react' import { axe } from 'jest-axe' import Home from '@/pages/index' it('should demonstrate this matcher`s usage with react testing library', async () => { const { container } = render(<Home/>) // 等待组件所有状态更新完成 await waitFor(() => expect(container.querySelector('img')).toBeInTheDocument()) const results = await axe(container) expect(results).toHaveNoViolations() })
该方案仅绕开了act警告,没有解决测试环境加载完整next/image带来的额外性能开销,优先选择方案1。
内容的提问来源于stack exchange,提问作者anonmily
相关产品推荐
相关产品推荐

