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

Jest+@testing-library/react-native测试自定义Input组件报ReferenceError错误

问题描述

开发了一款支持传入必选及可选props的React Native自定义Input组件,使用Jest与@testing-library/react-native进行单元测试,执行npm run test命令时出现报错,其余组件测试用例均可正常运行,仅Input组件测试失败,尝试移除部分props仍无法定位故障原因。

报错信息

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.

 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
    
    Check the render method of `Input`.
        at Input (/Users/jefflewis/Documents/Computer-Programming/Projects-Libraries/unsion/unison-ui-react-native/src/components/inputs/Input.tsx:24:20)

测试文件代码(Input.test.tsx)

// Imports: Dependencies
import React from 'react';
import renderer from 'react-test-renderer';
import { cleanup } from '@testing-library/react-native';

// Imports: Components
import Input from '../Input';

// React Native Testing Library: Cleanup (Unmounts Component And Destroys Container)
afterEach(cleanup);

// Tests: Input
describe('Input', () => {
  // Renders Component
  it('renders component', () => {
    renderer.create(<Input placeholder="Placeholder" value="Value" onChangeText={() => console.log('Hi')} />);
  });
});

组件文件代码(Input.tsx)

// Imports: Dependencies
import React, { useEffect, useState, useRef } from 'react';
import { Dimensions, StyleSheet, Text, TextInput, View } from 'react-native';

// Imports: Styles
import { defaultStyles } from '../../styles/styles';

// Imports: TypeScript Types
import { IInputProps } from '../../types/inputTypes';

// Screen Dimensions
const { width } = Dimensions.get('window');

// Component: Input
const Input: React.FC<IInputProps> = ({
  value,
  onChangeText,
  placeholder,
  placeholderTextColor,
  label,
  darkMode,
  autoCapitalize,
  autoCompleteType, // Android Only
  autoCorrect,
  autoFocus,
  blurOnSubmit,
  caretHidden,
  clearButtonMode, // iOS Only
  clearTextOnFocus, // iOS Only
  dataDetectorTypes, // iOS Only
  defaultValue,
  editable,
  enablesReturnKeyAutomatically, // iOS Only
  keyboardType,
  maxLength,
  multiline,
  numberOfLines, // iOS Only
  onSubmitEditing,
  returnKeyType,
  secureTextEntry,
  spellCheck, // iOS Only
  textAlign,
  textContentType, // iOS Only
  // passwordRules, // iOS Only
}): JSX.Element => {
  // React Hooks: State
  const [text, setText] = useState<string>('');

  // React Hooks: Refs
  const textInputRef: React.RefObject<TextInput> = useRef(null);

  // React Hooks: Lifecycle Methods
  useEffect(() => {
    // Set State
    setText(value);
  }, [value]);

  // Render Label
  const renderLabel = (): JSX.Element | undefined => {
    // Check If Prop Exists
    if (label) {
      return <Text style={darkMode ? styles.labelTextDark : styles.labelTextLight}>{label}</Text>;
    }
  };

  return (
    <View style={styles.container}>
      <>{renderLabel()}</>

      <TextInput
        ref={textInputRef}
        style={darkMode ? styles.inputTextDark : styles.inputTextLight}
        placeholder={placeholder}
        placeholderTextColor={placeholderTextColor ? defaultStyles.colorDarkLabelTertiary : defaultStyles.colorLightLabelTertiary}
        value={text}
        onChangeText={onChangeText}
        autoCapitalize={autoCapitalize || 'none'}
        autoCompleteType={autoCompleteType || 'off'} // Android Only
        autoCorrect={autoCorrect}
        autoFocus={autoFocus}
        blurOnSubmit={blurOnSubmit}
        caretHidden={caretHidden}
        clearButtonMode={clearButtonMode || 'never'} // iOS Only
        clearTextOnFocus={clearTextOnFocus} // iOS Only
        dataDetectorTypes={dataDetectorTypes || 'none'} // iOS Only
        defaultValue={defaultValue}
        editable={editable}
        enablesReturnKeyAutomatically={enablesReturnKeyAutomatically} // iOS Only
        keyboardAppearance={darkMode ? 'dark' : 'light'} // iOS Only
        keyboardType={keyboardType || 'default'}
        maxLength={maxLength || 1000}
        multiline={multiline}
        numberOfLines={numberOfLines || 1} // Android Only
        onSubmitEditing={onSubmitEditing}
        returnKeyType={returnKeyType || 'done'}
        secureTextEntry={secureTextEntry}
        spellCheck={spellCheck} // iOS Only
        textAlign={textAlign || 'left'}
        textContentType={textContentType || 'none'} // iOS Only
      />
    </View>
  );
};

// Styles
const styles = StyleSheet.create({
  container: {
    height: 'auto',
    width: width - defaultStyles.marginExtraLarge,
    marginBottom: defaultStyles.marginMedium,
  },
  labelTextLight: {
    color: defaultStyles.colorDarkLabelSecondary,
    fontSize: defaultStyles.fontSizeFootnoteSmall,
    fontWeight: defaultStyles.fontWeightSemiBold,
    letterSpacing: defaultStyles.fontLetterSpacingFootnote,
    textTransform: 'uppercase',
  },
  labelTextDark: {
    color: defaultStyles.colorLightLabelSecondary,
    fontSize: defaultStyles.fontSizeFootnoteSmall,
    fontWeight: defaultStyles.fontWeightSemiBold,
    letterSpacing: defaultStyles.fontLetterSpacingFootnote,
    textTransform: 'uppercase',
  },
  inputTextLight: {
    height: 40,
    fontSize: defaultStyles.fontSizeSubheading,
    color: defaultStyles.colorLightLabel,
    borderColor: defaultStyles.colorLightBorder,
    borderBottomWidth: StyleSheet.hairlineWidth,
    // textOverflow: 'ellipsis',
  },
  inputTextDark: {
    height: 40,
    fontSize: defaultStyles.fontSizeSubheading,
    color: defaultStyles.colorDarkLabel,
    borderColor: defaultStyles.colorDarkBorder,
    borderBottomWidth: StyleSheet.hairlineWidth,
    // textOverflow: 'ellipsis',
  },
});

// Exports
export default Input;
解决方案

以下是问题的根因和对应修复步骤:

  1. 测试库混用导致环境冲突
    你同时使用了react-test-renderer和@testing-library/react-native的cleanup逻辑,两个库的生命周期处理逻辑冲突会导致Jest环境提前销毁,触发import after torn down报错。建议统一使用@testing-library/react-native的渲染逻辑,替换测试文件内的渲染代码即可:
// 移除react-test-renderer导入
import { render, screen } from '@testing-library/react-native';
// 修改测试用例
it('renders component', () => {
  render(<Input placeholder="Placeholder" value="Value" onChangeText={() => console.log('Hi')} />);
  expect(screen.getByPlaceholderText('Placeholder')).toBeTruthy();
});

如果要继续使用react-test-renderer,直接删除@testing-library/react-native的cleanup导入和afterEach(cleanup)逻辑即可。

  1. 样式导入不规范
    报错里提到的type is invalid大概率来自你导入的defaultStyles对象,检查../../styles/styles文件的导出方式:如果是默认导出就改成import defaultStyles from '../../styles/styles',如果是命名导出确认导出的变量名没有拼写错误。测试环境下如果样式文件有副作用,也可以在Jest配置里对样式文件做mock:
// jest.config.js
module.exports = {
  moduleNameMapper: {
    '^../../styles/styles$': '<rootDir>/__mocks__/stylesMock.ts',
  }
};
// __mocks__/stylesMock.ts
export const defaultStyles = {
  marginExtraLarge: 32,
  marginMedium: 16,
  colorDarkLabelSecondary: '#000',
  // 补全所有用到的样式字段即可
};
  1. 空返回值兼容问题
    renderLabel方法在没有传入label时返回undefined,空标签<>{undefined}</>在部分Jest模拟环境下会被识别为无效类型,修改render部分的逻辑即可:
// 替换<>{renderLabel()}</>为
{label && renderLabel()}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 04:54:02