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

如何正确测试使用Next.js dynamic懒加载的React组件?

Next.js 动态导入组件的单元测试解决方案

问题背景

基于React 18/Next.js 12的组件中,使用dynamic导入ReactMarkdown组件并禁用SSR:

const ReactMarkdown = dynamic(() => import('@/components/ReactMarkdown/ReactMarkdown'), { ssr: false})

const MyComponent = () => {
    // ... 逻辑处理,点击按钮更新mdContent
    return (
       <div><ReactMarkdown content={mdContent} /></div>
    )
}

点击按钮后mdContent会更新为纯文本(如'Here is a text'),但单元测试时Jest/React Testing Library会将动态组件渲染为<dynamic content="Here is a text" />结构,导致screen.getByText无法找到目标文本,测试失败。测试代码示例:

it('will render appropriate content', async () => {
  render(<MyComponent />)
  const myButton = screen.getByText('The Button')
  fireEvent.click(myButton)

  await waitFor(() => {
    expect(screen.getByText('Here is a text')).toBeInDocument()
  })
})

解决方法

方法一:测试环境直接导入组件

在组件中根据环境变量判断,测试环境下跳过动态导入,直接引入ReactMarkdown:

import dynamic from 'next/dynamic';

let ReactMarkdown;
if (process.env.NODE_ENV === 'test') {
  // 测试环境直接导入组件
  ReactMarkdown = require('@/components/ReactMarkdown/ReactMarkdown').default;
} else {
  // 生产/开发环境使用动态导入
  ReactMarkdown = dynamic(() => import('@/components/ReactMarkdown/ReactMarkdown'), { ssr: false });
}

const MyComponent = () => {
    // ... 组件逻辑
    return (
       <div><ReactMarkdown content={mdContent} /></div>
    )
}

方法二:Mock Next.js的dynamic函数

在测试文件中,mock next/dynamic,让它直接返回真实的ReactMarkdown组件,避免渲染占位的<dynamic>标签:

// 在测试文件顶部添加mock
jest.mock('next/dynamic', () => () => {
  const ReactMarkdown = require('@/components/ReactMarkdown/ReactMarkdown').default;
  return ReactMarkdown;
});

it('will render appropriate content', async () => {
  render(<MyComponent />)
  const myButton = screen.getByText('The Button')
  fireEvent.click(myButton)

  await waitFor(() => {
    expect(screen.getByText('Here is a text')).toBeInDocument()
  })
})

方法三:查询动态组件内的文本(不推荐)

如果不想修改组件或mock,可以直接定位<dynamic>元素,再查询其内部文本:

it('will render appropriate content', async () => {
  render(<MyComponent />)
  const myButton = screen.getByText('The Button')
  fireEvent.click(myButton)

  await waitFor(() => {
    // 先找到dynamic元素(建议给ReactMarkdown添加data-testid属性)
    const dynamicComponent = screen.getByTestId('dynamic-markdown');
    expect(screen.getByText('Here is a text', { container: dynamicComponent })).toBeInDocument()
  })
})

注意:这种方法依赖动态组件的渲染结构,稳定性较差,优先推荐前两种方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 00:17:10