React Native组件测试Jest语法错误求助:TouchableOpacity解析失败
React Native TypeScript Jest组件测试语法错误修复
问题概述
TypeScript React Native应用中,单元测试可正常执行,但编写TouchableOpacity组件测试时,Jest抛出SyntaxError,提示react-native库中TouchableOpacity.js文件存在未初始化的const声明。已配置Babel转换规则和transformIgnorePatterns,但仍无法完成文件转换,导致测试失败。
相关代码与配置
测试文件
// ButtonComponent.test.js import React from 'react'; import {render, fireEvent} from '@testing-library/react-native'; import {TouchableOpacity, Text} from 'react-native'; describe('ButtonComponent', () => { it('calls onPress when pressed', () => { const mockOnPress = jest.fn(); const {getByText} = render( <TouchableOpacity onPress={mockOnPress}> <Text>Click Me</Text> </TouchableOpacity>, ); fireEvent.press(getByText('Click Me')); expect(mockOnPress).toHaveBeenCalled(); }); });
Babel配置
module.exports = { presets: [ 'module:metro-react-native-babel-preset', '@babel/preset-react', '@babel/preset-typescript', '@babel/preset-env', ], plugins: [ [ 'module-resolver', { alias: { '@': './src', }, }, ], [ 'module:react-native-dotenv', { moduleName: '@env', path: '.env', }, ], ], };
Jest配置
module.exports = { preset: 'react-native', transform: { '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest', }, moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json', 'node'], moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1', '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileMock.js', '^@env$': '<rootDir>/src/types/env.d.ts', }, transformIgnorePatterns: [ 'node_modules/(?!(react-native|@react-native|@react-native(-community)|react-native-gesture-handler|react-native-reanimated|@react-navigation)/)', ], testPathIgnorePatterns: ['/node_modules/', '/dist/'], testEnvironment: 'node', collectCoverage: true, coverageReporters: ['json', 'lcov', 'text', 'clover'], verbose: true, };
TypeScript配置
{ "extends": "@react-native/typescript-config/tsconfig.json", "compilerOptions": { "jsx": "react-native", "allowJs": true, "baseUrl": ".", "paths": { "@/*": ["./src/*"] }, "allowSyntheticDefaultImports": true, "strict": true, "esModuleInterop": true, "resolveJsonModule": true }, "typeRoots": ["./src/types", "./node_modules/@types"], "include": ["src/**/*", "./App.tsx", "__tests__"] }
package.json
{ "name": "zero", "version": "0.0.1", "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "lint": "eslint .", "start": "react-native start", "test": "jest .", "test:watch": "jest --watch", "test:coverage": "jest --collectCoverage --coverageDirectory=\"./coverage\"", "commit": "cz", "prepare": "husky", "pretty": "prettier --write ." }, "dependencies": { "@babel/core": "^7.26.7", "@babel/runtime": "^7.26.7", "@react-navigation/bottom-tabs": "^7.2.0", "@react-navigation/native": "^7.0.14", "@react-navigation/native-stack": "^7.2.0", "@reduxjs/toolkit": "^2.5.1", "axios": "^1.7.9", "axios-mock-adapter": "^2.1.0", "react": "18.3.1", "react-native": "^0.77.0", "react-native-error-boundary": "^1.2.7", "react-native-fast-image": "^8.6.3", "react-native-gesture-handler": "^2.22.1", "react-native-keychain": "^9.2.2", "react-native-reanimated": "^3.16.7", "react-native-safe-area-context": "^5.1.0", "react-native-screens": "^4.6.0", "react-native-splash-screen": "^3.3.0", "react-native-toast-message": "^2.2.1", "react-native-vector-icons": "^10.2.0", "react-redux": "^9.2.0" }, "devDependencies": { "@babel/plugin-transform-react-jsx": "^7.25.9", "@babel/preset-env": "^7.26.7", "@babel/preset-react": "^7.26.3", "@babel/preset-typescript": "^7.26.0", "@react-native-community/cli": "15.0.1", "@react-native-community/cli-platform-android": "15.0.1", "@react-native-community/cli-platform-ios": "15.0.1", "@react-native/babel-preset": "^0.77.0", "@react-native/eslint-config": "^0.77.0", "@react-native/metro-config": "^0.77.0", "@react-native/typescript-config": "^0.77.0", "@testing-library/react-native": "^12.9.0", "@types/jest": "^29.5.14", "@types/react": "^18.3.18", "@types/react-native": "^0.72.8", "@types/react-native-dotenv": "^0.2.2", "@types/react-native-vector-icons": "^6.4.18", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.7.0", "babel-plugin-module-resolver": "^5.0.2", "commitizen": "^4.3.1", "cz-conventional-changelog": "^3.3.0", "eslint": "^8.19.0", "eslint-plugin-testing-library": "^7.1.1", "husky": "^9.1.7", "jest": "^29.7.0", "jest-react-native": "^18.0.0", "metro-react-native-babel-preset": "^0.77.0", "prettier": "2.8.8", "react-native-dotenv": "^3.4.11", "react-test-renderer": "^18.2.0", "ts-jest": "^29.2.5", "typescript": "5.0.4" }, "engines": { "node": ">=18" }, "config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } } }
修复步骤
1. 调整Jest测试环境
将testEnvironment从node改为jsdom,React Native组件测试需要DOM环境支持:
// jest.config.js module.exports = { // ...其他配置 testEnvironment: 'jsdom', }
也可直接移除该配置,使用react-native预设的默认测试环境。
2. 简化Babel预设
metro-react-native-babel-preset已包含React和环境转换逻辑,移除冗余预设,仅保留TypeScript预设:
// babel.config.js module.exports = { presets: [ 'module:metro-react-native-babel-preset', '@babel/preset-typescript', ], plugins: [ [ 'module-resolver', { alias: { '@': './src', }, }, ], [ 'module:react-native-dotenv', { moduleName: '@env', path: '.env', }, ], ], };
3. 优化transformIgnorePatterns正则
确保react-native内部所有模块都被正确转换,修正正则表达式:
// jest.config.js module.exports = { // ...其他配置 transformIgnorePatterns: [ 'node_modules/(?!((react-native|@react-native(-community)?)|react-native-gesture-handler|react-native-reanimated|@react-navigation)/)', ], }
4. 删除过时依赖
移除devDependencies中的jest-react-native,该依赖与react-native预设冲突:
// package.json "devDependencies": { // 移除 "jest-react-native": "^18.0.0" }
5. 统一测试文件后缀
将测试文件ButtonComponent.test.js重命名为ButtonComponent.test.tsx,匹配TypeScript项目规范,确保Babel正确识别并转换。
完成上述修改后,重新运行组件测试即可解决语法错误,模拟按钮点击的测试逻辑将正常执行。
内容的提问来源于stack exchange,提问作者shirxz
相关产品推荐
相关产品推荐

