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

React Native(Expo)项目运行Jest报意外token错误如何解决

问题描述

在基于Expo的React Native项目中配置Jest单元测试环境时,执行测试命令抛出如下错误:
Jest encountered an unexpected token ; SyntaxError: Invalid or unexpected token
错误截图如下:
Jest运行报错截图
已尝试Jest、Expo、React Native官方文档提及的所有配置方案,问题始终未解决,调整配置后还会抛出其他不同类型的错误。以下为项目相关配置与待测试文件代码。

现有项目配置

package.json

{
  "name": "contact",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject",
    "test": "jest"
  },
  "jest": {
    "preset": "react-native",
    "transformIgnorePatterns": [
      "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|)"
    ]
  },
  "dependencies": {
    "@react-native-community/masked-view": "^0.1.11",
    "@reduxjs/toolkit": "^1.8.2",
    "expo": "~45.0.0",
    "expo-image-picker": "~13.1.1",
    "expo-media-library": "~14.1.0",
    "expo-status-bar": "~1.3.0",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-native": "0.68.2",
    "react-native-gesture-handler": "~2.2.1",
    "react-native-image-picker": "^4.8.4",
    "react-native-reanimated": "~2.8.0",
    "react-native-safe-area-context": "4.2.4",
    "react-native-screens": "~3.11.1",
    "react-native-web": "0.17.7",
    "react-navigation": "^4.4.4",
    "react-navigation-stack": "^2.10.4",
    "react-redux": "^8.0.2",
    "redux": "^4.2.0"
  },
  "devDependencies": {
    "@babel/core": "^7.12.9",
    "@babel/preset-env": "^7.18.6",
    "@babel/preset-react": "^7.18.6",
    "@testing-library/react-native": "^10.1.1",
    "@types/jest": "^28.1.4",
    "babel-jest": "^28.1.2",
    "jest": "^28.1.2",
    "jest-environment-jsdom": "^28.1.2",
    "ts-jest": "^28.0.5",
    "typescript": "^4.7.4"
  },
  "private": true
}

babel.config.js

module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
  };
};

待测试页面文件 indexScreen.js

import React, {useState} from 'react';
import {View, Text, StyleSheet, FlatList, TouchableOpacity} from 'react-native';
import { Feather } from '@expo/vector-icons';
import {deleteContact} from '../redux/contactSlice';
import { useSelector,useDispatch } from 'react-redux';
import SearchBar from '../components/searchBar';

const indexScreen= function({navigation}) {
    const value = useSelector((state) => state.contacts);
    const dispatch = useDispatch();
    const [term,setTerm] = useState("");
    const [filteredValue, setFilteredValue] = useState([...value]);

    const searchFilter = (text) =>{
        setTerm(text);
        if (term !==""){
            const result = value.filter((item) => {
                return item.name.toLowerCase().includes(term.toLowerCase())
            });
            setFilteredValue(result);

        }else if (term === ""){
            setFilteredValue([...value]);
        }
        
    }

    return(
        <View>
            <SearchBar
                term= {term}
                onTermChange = {newTerm => searchFilter(newTerm)}
            />
            <Text>{term}</Text>
            <FlatList
                data= {term.length < 1 ? value:filteredValue}
                keyExtrator = {(contact) => contact.id}

                renderItem = {({item}) => {
                    return(
                        <TouchableOpacity onPress = {() => navigation.navigate('Show', {id: item.id})}>
                            <View style ={styles.row}>
                                <Text style = {styles.title}> {item.name}</Text>
                                <TouchableOpacity onPress ={() => dispatch(deleteContact(item.id))}>
                                    <Feather style={styles.icon} name="trash" />
                                </TouchableOpacity>
                            </View>
                        </TouchableOpacity>
                    )
                }}

            />
        </View>
    )
}

indexScreen.navigationOptions = ({navigation}) => {
    return {
        headerRight: () => (
            <TouchableOpacity onPress={() => navigation.navigate('Create')}>
                <Feather name = 'plus' size = {30}/>
            </TouchableOpacity>
        ),

    }
};

const styles = StyleSheet.create({
    row:{
        flexDirection:'row',
        justifyContent:'space-between',
        paddingVertical:20,
        paddingHorizontal:10,
        borderTopWidth:1,
        borderColor:'grey'
    },
    title:{
        fontSize:18
    },
    icon: {
        fontSize:20
    }
});

export default indexScreen;
问题修复方案

报错核心原因是Jest转译配置不符合Expo项目规则,存在3个配置错误:

  • Jest preset使用错误:纯react-native preset仅适用于React Native裸项目,Expo项目必须使用专属的jest-expo preset,否则无法正确转译Expo生态下的依赖包
  • transformIgnorePatterns正则语法错误:规则末尾多余的|导致正则匹配逻辑失效,需要转译的node_modules包未被处理,直接输出ES语法导致Jest解析失败
  • Babel测试环境转译规则缺失:缺少CommonJS转译插件,Jest运行在CommonJS环境下无法直接识别ES模块语法

按以下步骤修复:

  1. 安装缺失依赖
    执行命令安装Expo专属Jest preset、对应React版本的测试渲染器、CommonJS转译插件:
    npm install --save-dev jest-expo react-test-renderer@17.0.2 @babel/plugin-transform-modules-commonjs
    如果不需要测试Web端代码,直接卸载jest-environment-jsdom避免环境冲突:npm uninstall jest-environment-jsdom

  2. 修正package.json中的Jest配置
    替换原有jest字段内容,修复正则错误、更换正确preset、补充redux相关依赖的转译白名单:

"jest": {
  "preset": "jest-expo",
  "transformIgnorePatterns": [
    "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg|react-redux|@reduxjs/toolkit)"
  ],
  "moduleFileExtensions": ["js", "jsx", "ts", "tsx", "json", "node"]
}
  1. 修正babel.config.js配置
    添加测试环境下的转译规则,修改后内容如下:
module.exports = function(api) {
  api.cache(true);
  return {
    presets: ['babel-preset-expo'],
    env: {
      test: {
        plugins: ['@babel/plugin-transform-modules-commonjs']
      }
    }
  };
};
  1. 清除缓存重新运行测试
    执行命令清除Jest历史缓存后重新运行测试即可解决语法报错:
    npx jest --clearCache && npm test

测试编写注意事项

由于待测试组件依赖Redux的useSelector、useDispatch hook,编写测试用例时需要将组件包裹在Redux Provider中,否则会抛出hook调用错误,示例测试代码如下:

import React from 'react';
import { render } from '@testing-library/react-native';
import { Provider } from 'react-redux';
import store from '../redux/store'; // 替换为项目实际的store文件路径
import IndexScreen from './indexScreen';

test('页面可正常渲染', () => {
  const mockNavigation = { navigate: jest.fn() };
  render(
    <Provider store={store}>
      <IndexScreen navigation={mockNavigation} />
    </Provider>
  );
  // 补充对应断言逻辑
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 17:06:27