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

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源码位置

根因

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:18:15