Next.js 12+Jest 28运行测试时SVG转换失败引发组件渲染报错
Next.js 12 + Jest 28 导入SVG组件测试报「Element type is invalid」修复方案
问题场景
- 技术栈版本:Next.js
12.0.7、Jest28.1.0,通过next-react-svg实现组件内SVG资源直接作为组件导入 - 已尝试方案:在
jest.config.js中配置自定义转换规则,指向__mocks__目录下自行编写的jest-svg-transformer.js,将SVG文件转换为可渲染的React函数组件;后续替换为官方jest-svg-transformer库处理SVG转换,问题未解决 - 报错表现:执行
yarn test或jest命令运行单元测试时,引入Logo.svg组件的Loader组件渲染抛出错误:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object
错误提示指向Loader组件的render方法。
根因说明
报错本质是Jest处理SVG导入后,返回值不是可渲染的React组件类型,而是普通对象/字符串,核心诱因有3个:
- 转换规则优先级错误:SVG文件被静态资源通用转换规则提前处理为文件路径对象,没有走到SVG专用的组件转换逻辑
- Transformer导出格式不兼容:自定义转换脚本没有同时适配
next-react-svg支持的默认导入、命名导入ReactComponent两种写法,存在ESM/CommonJS互操作问题 - Jest缓存干扰:修改转换配置后旧的转换缓存未清除,一直使用错误的旧转换结果
修复步骤
1. 修正jest.config.js配置优先级
将SVG的转换规则放在所有transform规则的最顶部(Jest按规则从上到下匹配,先匹配到的规则优先生效),移除moduleNameMapper中对SVG文件的匹配规则,避免和transform逻辑冲突:
// jest.config.js module.exports = { testEnvironment: 'jsdom', setupFilesAfterEnv: ['<rootDir>/jest.setup.js'], transform: { // SVG转换规则必须放在最前面 '^.+\\.svg$': '<rootDir>/__mocks__/jest-svg-transformer.js', // 如果使用官方jest-svg-transformer,替换上面一行为: // '^.+\\.svg$': 'jest-svg-transformer', '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], }, moduleNameMapper: { // 其他静态资源(图片、字体等)的mock规则不要包含svg后缀 '^.+\\.(png|jpg|jpeg|gif|webp|woff|woff2|ttf)$': '<rootDir>/__mocks__/fileMock.js', // 路径别名配置保留 '@/(.*)$': '<rootDir>/src/$1', }, // 确保next-react-svg不被忽略转换 transformIgnorePatterns: [ '/node_modules/(?!next-react-svg)', ], }
如果使用官方jest-svg-transformer,请先安装适配Jest 28的版本:
yarn add -D jest-svg-transformer@^1.1.0
2. 修正自定义SVG转换脚本
如果使用自写的__mocks__/jest-svg-transformer.js,确保导出逻辑同时兼容两种SVG导入方式:
// __mocks__/jest-svg-transformer.js module.exports = { process: (_, filePath) => { // 提取SVG文件名生成组件名,避免重名冲突 const fileName = filePath.split(/[\\/]/).pop(); const componentName = fileName.replace(/\.svg$/, 'SvgMock').replace(/[^a-zA-Z0-9]/g, '_'); return ` const React = require('react'); function ${componentName}(props) { return React.createElement('svg', { 'data-testid': 'mock-svg', ...props }, null); } ${componentName}.displayName = '${componentName}'; module.exports = ${componentName}; module.exports.default = ${componentName}; module.exports.ReactComponent = ${componentName}; `; }, };
3. 统一组件内SVG导入写法
确保Loader组件中导入SVG的写法和next.config.js里next-react-svg的配置一致,不要混用导入方式:
// 示例:Loader组件正确写法 import Logo from './Logo.svg'; // 如果next-react-svg配置为仅支持命名导入,则使用: // import { ReactComponent as Logo } from './Logo.svg'; export default function Loader() { return ( <div className="loader-container"> <Logo className="loader-logo" width={40} height={40} /> </div> ); }
4. 清除Jest缓存重新执行测试
Jest默认会缓存所有文件的转换结果,修改配置后必须先清除缓存再运行测试:
yarn test --clearCache yarn test
验证方式
修复后测试用例可正常渲染Loader组件,不会抛出类型错误,可正常断言SVG元素存在:
import { render } from '@testing-library/react'; import Loader from './Loader'; test('loader renders logo correctly', () => { const { getByTestId } = render(<Loader />); expect(getByTestId('mock-svg')).toBeInTheDocument(); });
内容的提问来源于stack exchange,提问作者bonnopc
相关产品推荐
相关产品推荐

