React Native自定义Native Module引发单元测试报错如何解决
问题根因
TypeScript类型声明文件仅作用于编译阶段的类型校验,不会改变运行时代码逻辑。单元测试运行在Node/jsdom环境,不会加载Android/iOS端的原生代码,因此NativeModules.SomeModule在测试环境下默认值为undefined,访问其HelloWorld属性时就会抛出TypeError: Cannot read property 'HelloWorld' of undefined错误,和类型声明是否编写无关。
解决方案
方案1:测试框架层Mock原生模块(推荐)
以最常用的Jest测试框架为例,通过Mock注入原生模块的模拟实现,让测试环境下可以正常访问模块方法,有两种常见实现:
- 全局Mock:在项目根目录或测试目录下创建
__mocks__/react-native.js文件,全局替换react-native导出的NativeModules对象
const actualReactNative = jest.requireActual('react-native'); actualReactNative.NativeModules.SomeModule = { HelloWorld: jest.fn((addedText) => { // 可根据测试需求自定义模拟返回值 return addedText; }) }; export default actualReactNative;
- 单用例Mock:在具体测试文件的钩子函数中临时注入Mock,避免全局污染
import { NativeModules } from 'react-native'; import { helloWorld } from './path-to-your-native-module-file'; describe('Native module method test', () => { const mockHelloWorld = jest.fn(); beforeEach(() => { // 每个用例执行前重置Mock实现 mockHelloWorld.mockReset(); NativeModules.SomeModule = { HelloWorld: mockHelloWorld }; }); it('should invoke HelloWorld with passed parameter', () => { const testParam = 'test content'; helloWorld(testParam); expect(mockHelloWorld).toHaveBeenCalledWith(testParam); }); });
方案2:业务代码层增加空值兜底
在JS侧引用原生模块时增加空值判断,给非原生环境(测试、Web端等)提供兜底实现,从根源避免undefined访问错误:
import { NativeModules } from 'react-native'; // 原生模块不存在时使用兜底实现 const Thing = NativeModules.SomeModule ?? { HelloWorld: (addedText: string) => { // 非原生环境自定义逻辑,比如打印日志、返回模拟值 console.log('non-native env HelloWorld called:', addedText); return addedText; } }; export const helloWorld = (addedText: string) => { return Thing.HelloWorld(addedText); }; export default Thing;
补充说明
你之前编写的d.ts类型声明写法存在错误,正确的类型声明需要扩展React Native的NativeModules类型定义,参考写法如下:
// someModule.d.ts import 'react-native'; declare module 'react-native' { interface NativeModulesStatic { SomeModule: { HelloWorld: (addedText: string) => void; } } }
注意:类型声明仅用于TS编译时的类型校验和提示,不会生成任何运行时代码,因此仅补充d.ts文件无法解决单元测试的运行时报错。
内容的提问来源于stack exchange,提问作者Mr Heelis
相关产品推荐
相关产品推荐

