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

Expo项目Jest测试App组件遇TypeError: Cannot read properties of null (reading 'match')

Expo + Jest测试App组件时出现TypeError: Cannot read properties of null (reading 'match')问题

错误详情

TypeError: Cannot read properties of null (reading 'match')

1 | import 'react-native-gesture-handler';
> 2 | import { registerRootComponent } from 'expo'; 
    | ^
  3 | import React, { useState, useEffect,useCallback } from "react";
  4 | import { useFonts } from 'expo-font';
  5 | import * as SplashScreen from 'expo-splash-screen';

  at getDevServer (node_modules/react-native/Libraries/Core/Devtools/getDevServer.js:31:29)
  at Object.<anonymous> (node_modules/expo/src/environment/logging.fx.ts:15:39)
  at Object.<anonymous> (node_modules/expo/src/Expo.fx.tsx:3:1)
  at Object.<anonymous> (node_modules/expo/src/Expo.ts:1:1)
  at Object.<anonymous> (src/App.tsx:2:1)
  at Object.<anonymous> (src/__tests__/navigation/App.test.tsx:9:1)

项目相关配置与代码

package.json

"dependencies": {
 "jest": "^29.2.1",
  "jest-expo": "^49.0.0",
 "react-test-renderer": "^18.2.0",
 "ts-node": "^10.9.1",
},
"devDependencies": {
    "@babel/core": "^7.20.0",
    "@jest/types": "^29.6.3",
    "@testing-library/jest-dom": "^6.1.4",
    "@testing-library/jest-native": "^5.4.3",
    "@testing-library/react-native": "^12.3.0",
    "@types/jest": "^29.5.6",
    "@types/react": "~18.2.14",
    "@types/react-native": "^0.72.2",
    "@typescript-eslint/eslint-plugin": "^6.8.0",
    "@typescript-eslint/parser": "^6.8.0",
    "eslint": "^8.2.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-config-universe": "^12.0.0",
    "eslint-plugin-prettier": "^5.0.1",
    "eslint-plugin-react": "^7.33.2",
    "prettier": "^3.0.3",
    "ts-jest": "^29.1.1",
    "typescript": "^5.1.3"
  },

App.tsx

import 'react-native-gesture-handler';
import { registerRootComponent } from 'expo';
import React, { useState, useEffect,useCallback } from "react";
import { useFonts } from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import { StyleSheet, Text, View,ScrollView,SafeAreaView,PixelRatio } from 'react-native';
import RootDrawerNavigator from './navigation/rootDrawerNavigator/RootDrawerNavigator'

SplashScreen.preventAutoHideAsync();
const App = () =>  {
  const designResolution = {
    width: 1528,
    height: 750
   }
   console.log(PixelRatio.getFontScale())

  const [fontsLoaded, fontError] = useFonts({
    'DMRegular': require('./assets/fonts/DMSans-Regular.ttf'),
    'DMMedium': require('./assets/fonts/DMSans-Medium.ttf'),
    'DMBold': require('./assets/fonts/DMSans-Bold.ttf'),
    'DMLight': require('./assets/fonts/DMSans-ExtraLight.ttf'),
  });
  const onLayoutRootView = useCallback(async () => {
    if (fontsLoaded || fontError) {
      await SplashScreen.hideAsync();
    }
  }, [fontsLoaded, fontError]);

  if (!fontsLoaded && !fontError) {
    return null;
  }
  return (
        <RootDrawerNavigator />
  );
}

registerRootComponent(App);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

export default App

App.test.tsx

import * as React from 'react';
import { screen, render, fireEvent ,waitFor} from '@testing-library/react-native';
import {expect, jest, test} from '@jest/globals';
import '@testing-library/jest-native/';
import { NavigationContainer } from '@react-navigation/native';
import renderer from "react-test-renderer"
import { ClientStoreStack, RootDrawerNavigator } from '../../navigation';
import { DashboardScreen } from '../../screens';
import  App  from '../../App';


jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');

describe('render correctly', () => {
  it('renders App', () => {
    const tree = renderer
    .create(<App />).toJSON();
  expect(tree).toMatchSnapshot();
  });
});

jest.config.ts

import type {Config} from 'jest';

const config: Config = {
  verbose: true,

    "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)"
    ],
    "collectCoverage": true,
    "collectCoverageFrom": [
      "**/*.{ts,tsx}",
      "!**/coverage/**",
      "!**/node_modules/**",
      "!**/babel.config.js",
      "!**/jest.setup.ts",
      "!**/pettierrc.js",
      "!**/eslintrc.js"
    ],
    "setupFilesAfterEnv": ["@testing-library/jest-native/extend-expect",
    "<rootDir>/src/jest.setup.ts"],
    "setupFiles": [
      
    ],
    
};

export default config;

setup.jest.ts(React Navigation配置)

import '@testing-library/jest-native/extend-expect';
import 'react-native-gesture-handler/jestSetup';
import {expect, jest, test} from '@jest/globals';


jest.mock('react-native-reanimated', () => {
  const Reanimated = require('react-native-reanimated/mock');

  Reanimated.default.call = () => {};

  return Reanimated;
});

jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');

解决方法

1. 补充全局window.location mock

在setup.jest.ts中添加以下代码,模拟浏览器环境的location对象,解决getDevServer函数的依赖问题:

// Mock window.location to fix getDevServer error
global.window = {
  ...global.window,
  location: {
    href: 'http://localhost:8081',
    protocol: 'http:',
    host: 'localhost:8081',
    hostname: 'localhost',
    port: '8081',
    pathname: '/',
    search: '',
    hash: '',
    origin: 'http://localhost:8081',
  },
};

2. Mock Expo相关模块

在setup.jest.ts中添加对Expo模块的mock,避免测试时加载不必要的环境初始化代码:

// Mock expo modules
jest.mock('expo', () => ({
  registerRootComponent: jest.fn(),
}));

// Mock expo-splash-screen
jest.mock('expo-splash-screen', () => ({
  preventAutoHideAsync: jest.fn(),
  hideAsync: jest.fn(),
}));

// Mock expo-font
jest.mock('expo-font', () => ({
  useFonts: jest.fn(() => [true, null]), // 模拟字体已加载完成
}));

3. 调整测试文件(可选)

如果测试只需要验证组件渲染,可以直接测试RootDrawerNavigator而非整个App,跳过Expo的根组件注册逻辑:

// 修改App.test.tsx
import * as React from 'react';
import renderer from "react-test-renderer"
import { RootDrawerNavigator } from '../../navigation';
import { NavigationContainer } from '@react-navigation/native';

jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');

describe('render correctly', () => {
  it('renders RootDrawerNavigator', () => {
    const tree = renderer
    .create(
      <NavigationContainer>
        <RootDrawerNavigator />
      </NavigationContainer>
    ).toJSON();
  expect(tree).toMatchSnapshot();
  });
});

4. 验证配置

确保jest.config.ts中的setupFilesAfterEnv正确引入了setup.jest.ts,并且所有mock逻辑在测试执行前加载。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 13:35:00