为何Jest测试中BroadcastChannel类未定义?
问题原因及解决方案
原因
Jest 运行在 Node.js 环境中,而 BroadcastChannel 是浏览器专属的 Web API,Node.js 本身并未提供这个全局对象。另外你在自定义 Hook 外部直接实例化 BroadcastChannel,这部分代码会在模块加载阶段就执行,此时测试环境还未对该 API 做模拟,因此直接抛出未定义的错误。
解决办法
方案1:全局模拟 BroadcastChannel
在 Jest 的全局配置文件(如 jest.setup.js)中添加对 BroadcastChannel 的模拟:
global.BroadcastChannel = jest.fn(() => ({ postMessage: jest.fn(), onmessage: null, close: jest.fn() }));
然后在 jest.config.js 中配置该文件为测试前置脚本:
module.exports = { setupFilesAfterEnv: ['./jest.setup.js'], // 其他 Jest 配置项 };
这样所有测试用例执行前都会自动注入这个模拟对象,避免未定义错误。
方案2:在 Hook 内部条件实例化
将 BroadcastChannel 的实例化逻辑移到 Hook 内部,同时判断当前环境是否支持该 API,测试环境下返回模拟对象:
import React from 'react'; export default function useSessionTimeout() { const [sessionChannel] = React.useState(() => { // 仅在浏览器环境中实例化真实的 BroadcastChannel if (typeof BroadcastChannel !== 'undefined') { return new BroadcastChannel("session_timeout_channel"); } // 测试环境返回空实现的模拟对象 return { postMessage: () => {}, onmessage: null, close: () => {} }; }); // 你的会话超时逻辑... return { /* 返回需要的方法或状态 */ }; }
这种方式避免了模块加载阶段执行浏览器 API 代码,同时兼容测试环境。
方案3:验证消息逻辑(可选)
如果需要测试消息发送/接收的逻辑,可以在测试用例中自定义模拟实现,以便断言调用:
import { renderHook } from '@testing-library/react'; import useSessionTimeout from './useSessionTimeout'; describe('useSessionTimeout', () => { it('should broadcast session timeout message', () => { // 自定义模拟的 postMessage 方法 const mockPostMessage = jest.fn(); global.BroadcastChannel.mockImplementation(() => ({ postMessage: mockPostMessage, onmessage: null, close: jest.fn() })); // 渲染 Hook const { result } = renderHook(() => useSessionTimeout()); // 触发发送消息的操作(根据你的 Hook 逻辑调整) // result.current.triggerSessionTimeout(); // 断言消息是否正确发送 expect(mockPostMessage).toHaveBeenCalledWith({ type: 'SESSION_TIMEOUT' }); }); });
内容的提问来源于stack exchange,提问作者melix
相关产品推荐
相关产品推荐

