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

如何用Jest Mock React Native中的DeviceEventEmitter?

Jest Mock React Native DeviceEventEmitter 解决方案

你之前的两种mock方式都存在问题:

  • 第一种只mock了addListener和remove,但实际DeviceEventEmitter的addListener会返回一个带remove方法的订阅实例,组件卸载时通常调用的是这个实例的remove,而非直接调用DeviceEventEmitter.remove;另外官方接口里移除监听器的方法是removeListener,不是remove。
  • 第二种直接返回实际模块,等于没有做mock,自然无法追踪监听器的调用情况。

下面是两种可行的实现方案:

方案1:完全Mock DeviceEventEmitter

直接模拟符合官方接口的结构,同时记录调用细节,方便测试断言。

// 定义mock函数,用于后续断言
const mockAddListener = jest.fn((eventName, callback) => ({
  remove: jest.fn() // 模拟订阅对象的remove方法
}));
const mockRemoveListener = jest.fn();
const mockEmit = jest.fn();

// 注意:如果你的代码是从'react-native'导入DeviceEventEmitter,这里要改成mock 'react-native'
jest.mock('react-native/Libraries/EventEmitter/RCTDeviceEventEmitter', () => ({
  addListener: mockAddListener,
  removeListener: mockRemoveListener,
  emit: mockEmit
}));

测试示例

import { render, cleanup } from '@testing-library/react-native';
import YourComponent from './YourComponent';
import DeviceEventEmitter from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter';

describe('YourComponent', () => {
  afterEach(() => {
    cleanup();
    jest.clearAllMocks(); // 清空mock调用记录
  });

  // 测试挂载时添加监听器
  it('registers event listener on mount', () => {
    render(<YourComponent />);
    expect(DeviceEventEmitter.addListener).toHaveBeenCalledWith('target-event-name', expect.any(Function));
  });

  // 测试卸载时移除监听器
  it('removes event listener on unmount', () => {
    const { unmount } = render(<YourComponent />);
    // 获取addListener返回的订阅实例
    const mockSubscription = mockAddListener.mock.results[0].value;
    unmount();
    // 如果组件调用的是订阅实例的remove方法
    expect(mockSubscription.remove).toHaveBeenCalled();
    // 如果组件调用的是DeviceEventEmitter.removeListener
    // expect(DeviceEventEmitter.removeListener).toHaveBeenCalledWith('target-event-name', expect.any(Function));
  });
});

方案2:Mock实际模块的指定方法

如果需要保留DeviceEventEmitter的部分原生功能,可以用jest.spyOn来mock指定方法:

import DeviceEventEmitter from 'react-native/Libraries/EventEmitter/RCTDeviceEventEmitter';

// 在测试文件顶部mock
jest.spyOn(DeviceEventEmitter, 'addListener').mockImplementation((eventName, callback) => {
  // 保留实际的订阅逻辑,但替换remove方法为mock
  const subscription = jest.requireActual('react-native/Libraries/EventEmitter/RCTDeviceEventEmitter').addListener(eventName, callback);
  subscription.remove = jest.fn(subscription.remove);
  return subscription;
});

jest.spyOn(DeviceEventEmitter, 'removeListener').mockImplementation(jest.requireActual('react-native/Libraries/EventEmitter/RCTDeviceEventEmitter').removeListener);

注意事项

如果你的业务代码是通过import { DeviceEventEmitter } from 'react-native';导入的,那么mock路径要改为react-native,示例如下:

jest.mock('react-native', () => ({
  ...jest.requireActual('react-native'), // 保留其他原生模块
  DeviceEventEmitter: {
    addListener: mockAddListener,
    removeListener: mockRemoveListener,
    emit: mockEmit
  }
}));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 01:57:26