配置ESM的Jest无法解析React Native组件导入,求助排查
问题:Jest配置ESM测试React Native组件时无法解析'react-native'导出
报错信息
SyntaxError: The requested module 'react-native' does not provide an export named 'StyleSheet'
环境版本
React Native: 0.70.4
代码示例
待测试组件
import React, { ReactElement } from 'react'; import { StyleSheet, Text, View } from 'react-native'; const { container } = StyleSheet.create({ container: { padding: 50, }, }); const ThisComponent = (): ReactElement => ( <View style={container}> <Text>Hello World!</Text> </View> ); export default ThisComponent;
测试代码
import React, { ReactElement } from 'react'; import { render } from '@testing-library/react-native'; import ThisComponent from '../ThisComponent'; describe('<OTPInputs>', () => { test('Renders without exploding', async () => { const { getByText } = render( <ThisComponent />, ); expect(getByText('Hello World!')).not.toBeDisabled(); }); });
Jest配置(package.json)
"jest": { "haste": { "defaultPlatform": "ios", "platforms": [ "android", "ios", "native" ] }, "resetMocks": true, "testEnvironment": "node", "testMatch": [ "**/src/**/*.(spec|test).[tj]s?(x)" ], "preset": "ts-jest/presets/default-esm", "transform": { "^.+\\.js$": "babel-jest" }, "transformIgnorePatterns": [ "node_modules/(?!((jest-)?react-native|@react-native(-community)?)/)" ], "extensionsToTreatAsEsm": [ ".ts", ".tsx" ], "globals": { "ts-jest": { "useESM": true } }, "setupFiles": [ "<rootDir>/node_modules/react-native/jest/setup.js" ], "setupFilesAfterEnv": [ "@testing-library/jest-native/extend-expect" ], "moduleNameMapper": { "^~/(.*)$": "<rootDir>/src/$1", "^~components/(.*)$": "<rootDir>/src/components/$1", "^~util/(.*)$": "<rootDir>/src/util/$1", "^~types/(.*)$": "<rootDir>/src/types/$1" } }
问题原因
React Native 0.70.x的主导出文件是使用特殊语法的CommonJS(CJS)格式,而ESM模式下的Jest对CJS导出的解析逻辑与常规ESM不同,无法正确识别StyleSheet、Text这类命名导出,从而抛出错误。
解决方案
方案1:手动Mock React Native的ESM兼容导出
使用jest.unstable_mockModule模拟符合ESM规范的react-native导出,同时测试代码需改为动态导入组件:
步骤1:添加Mock代码
在测试文件顶部添加以下mock:
jest.unstable_mockModule('react-native', () => ({ __esModule: true, StyleSheet: jest.requireActual('react-native/Libraries/StyleSheet/StyleSheet'), Text: jest.requireActual('react-native/Libraries/Text/Text'), TextInput: jest.requireActual('react-native/Libraries/Components/TextInput/TextInput'), TouchableOpacity: jest.requireActual('react-native/Libraries/Components/Touchable/TouchableOpacity'), View: jest.requireActual('react-native/Libraries/Components/View/View'), }));
步骤2:修改测试代码的组件导入方式
将静态导入改为动态导入:
// 替换原静态导入 const ThisComponent = (await import('../ThisComponent')).default;
修改后的完整测试代码:
import React from 'react'; import { render } from '@testing-library/react-native'; jest.unstable_mockModule('react-native', () => ({ __esModule: true, StyleSheet: jest.requireActual('react-native/Libraries/StyleSheet/StyleSheet'), Text: jest.requireActual('react-native/Libraries/Text/Text'), TouchableOpacity: jest.requireActual('react-native/Libraries/Components/Touchable/TouchableOpacity'), View: jest.requireActual('react-native/Libraries/Components/View/View'), })); describe('<ThisComponent>', () => { test('Renders without exploding', async () => { const ThisComponent = (await import('../ThisComponent')).default; const { getByText } = render(<ThisComponent />); expect(getByText('Hello World!')).not.toBeDisabled(); }); });
方案2:调整Jest配置适配RN的CJS导出
将Jest预设改回React Native官方预设,同时保留TS的ESM支持:
- 修改
preset为'react-native' - 调整
globals中的ts-jest配置,确保兼容RN的处理逻辑
修改后的Jest配置片段:
"jest": { // ...其他配置不变 "preset": "react-native", "globals": { "ts-jest": { "useESM": true, "tsconfig": "tsconfig.spec.json" // 可指定测试专用tsconfig } }, // ...其他配置不变 }
注意:使用官方预设时,需确保transformIgnorePatterns已正确包含react-native相关包,避免未转译的代码导致错误。
内容的提问来源于stack exchange,提问作者nerdlinger
相关产品推荐
相关产品推荐

