如何正确测试使用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
相关产品推荐
相关产品推荐

