Jest模拟单个window属性时React组件挂载报错的解决问询
你遇到的错误根源很明确:你完全替换了全局的window对象,而React在组件渲染过程中会依赖window上的很多其他属性(比如document、navigator、history等),你的mockWindow只包含了location.href,其他属性都是undefined,这就导致React内部执行到某些逻辑时(比如判断某个值是否是window实例)触发了instanceof的错误。
下面给你几种可行的解决方案,从简单到更优雅的都有:
方案一:仅mock window.location.href,保留其他原有属性
window.location是一个特殊的只读对象,不能直接赋值,所以我们可以用Object.defineProperty来修改它的href属性,同时保留原始location的其他属性:
// 保存原始的location对象,测试完成后恢复 const originalLocation = window.location; beforeAll(() => { // 复制原始location的所有属性,只修改href Object.defineProperty(window, 'location', { writable: true, value: { ...originalLocation, href: 'https://stackoverflow.com/questions' } }); }); afterAll(() => { // 测试结束后恢复原始的location,避免影响其他测试用例 Object.defineProperty(window, 'location', { value: originalLocation }); }); test('测试组件行为', () => { const wrapper = mount(<MyComponent />); // 这里可以添加你的测试断言,比如检查跳转逻辑是否正确 });
这种方式的好处是不会破坏window上的其他属性,React内部的逻辑可以正常运行。
方案二:采用依赖注入,彻底避免修改全局对象
这是更优雅的测试方案——把window作为组件的可选依赖传入,这样测试时可以直接传入模拟的对象,完全不需要修改全局window:
组件代码修改:
// MyComponent.jsx import React from 'react'; // 给组件添加一个可选的windowObj参数,默认值为全局window const MyComponent = ({ windowObj = window }) => { const handleNavigate = () => { windowObj.location.href = 'https://example.com'; }; return ( <button onClick={handleNavigate}>跳转</button> ); }; export default MyComponent;
测试代码:
// MyComponent.test.jsx import { mount } from 'enzyme'; import MyComponent from './MyComponent'; test('点击按钮会修改href', () => { // 只模拟我们需要的属性,完全不碰全局window const mockWindow = { location: { href: '' } }; const wrapper = mount(<MyComponent windowObj={mockWindow} />); // 触发点击事件 wrapper.find('button').simulate('click'); // 断言href是否被正确修改 expect(mockWindow.location.href).toBe('https://example.com'); });
这种方式的优势在于测试完全隔离,不会影响其他测试用例,也符合组件的可测试性设计原则。
为什么你的原代码会报错?
你之前的代码jest.spyOn(window, 'window', 'get').mockImplementation(() => mockWindow)是在替换window.window这个指向自身的引用,这会导致React内部所有访问window的地方都拿到你的mockWindow,而这个对象只有location.href,缺少React运行必需的其他属性(比如window.document),所以才会触发TypeError: Right-hand side of 'instanceof' is not an object的错误。
内容的提问来源于stack exchange,提问作者user2490003

