如何使用Jest测试ComponentDidMount生命周期及绑定的各类window事件
测试方案
以下测试基于Jest + Enzyme/React Testing Library实现,分为两部分验证:
1 验证componentDidMount调用及事件绑定正确性
核心逻辑是挂载组件前先监听window.addEventListener调用,验证调用次数、事件名、绑定的处理函数是否符合预期:
import { mount } from 'enzyme'; import MyComponent from './MyComponent'; // 替换为你的组件路径 describe('MyComponent componentDidMount', () => { it('should bind 4 specified window events after component mount', () => { const addEventListenerSpy = jest.spyOn(window, 'addEventListener'); const mockProcessFn = jest.fn(); const wrapper = mount(<MyComponent processAdminMessage={mockProcessFn} />); const componentInstance = wrapper.instance(); // 验证事件绑定完全符合预期 expect(addEventListenerSpy).toHaveBeenCalledTimes(4); expect(addEventListenerSpy).toHaveBeenCalledWith('message', componentInstance.handleMessage); expect(addEventListenerSpy).toHaveBeenCalledWith('beforeunload', componentInstance.handleBeforeUnload); expect(addEventListenerSpy).toHaveBeenCalledWith('blur', componentInstance.handleAppFocusOut); expect(addEventListenerSpy).toHaveBeenCalledWith('focus', componentInstance.handleAppFocusIn); // 测试后清理 addEventListenerSpy.mockRestore(); wrapper.unmount(); }); });
2 逐个验证事件处理函数执行逻辑
2.1 handleMessage测试
验证只有收到包含adminToClient字段的消息时,才会调用props传入的处理函数:
it('handleMessage should trigger processAdminMessage only when receive valid data', () => { const mockProcessFn = jest.fn(); const wrapper = mount(<MyComponent processAdminMessage={mockProcessFn} />); const componentInstance = wrapper.instance(); // 合法数据场景 componentInstance.handleMessage({ data: { adminToClient: 'test_cmd' } }); expect(mockProcessFn).toHaveBeenCalledWith('test_cmd'); // 非法数据场景(无adminToClient字段) componentInstance.handleMessage({ data: { otherKey: 'test' } }); // 非法数据场景(无data字段) componentInstance.handleMessage({}); expect(mockProcessFn).toHaveBeenCalledTimes(1); // 仅合法场景触发1次 });
2.2 handleBeforeUnload测试
验证触发时正确写入时间戳到sessionStorage:
it('handleBeforeUnload should save current timestamp to sessionStorage', () => { const setItemSpy = jest.spyOn(sessionStorage.__proto__, 'setItem'); const wrapper = mount(<MyComponent processAdminMessage={jest.fn()} />); const componentInstance = wrapper.instance(); const mockTimestamp = 1690000000000; // mock系统时间 jest.useFakeTimers().setSystemTime(mockTimestamp); componentInstance.handleBeforeUnload(); expect(setItemSpy).toHaveBeenCalledWith('lastVisit', mockTimestamp.toString()); // 测试后清理 jest.useRealTimers(); setItemSpy.mockRestore(); });
2.3 handleAppFocusOut测试
验证仅当焦点元素非BODY时,才会保存到state:
it('handleAppFocusOut should save non-BODY active element to state', () => { const wrapper = mount(<MyComponent processAdminMessage={jest.fn()} />); const componentInstance = wrapper.instance(); // 模拟焦点在input元素上 const mockInput = document.createElement('input'); Object.defineProperty(document, 'activeElement', { value: mockInput, configurable: true }); componentInstance.handleAppFocusOut(); expect(wrapper.state('lastFocusedField')).toBe(mockInput); // 模拟焦点在BODY上 Object.defineProperty(document, 'activeElement', { value: document.body, configurable: true }); componentInstance.handleAppFocusOut(); expect(wrapper.state('lastFocusedField')).toBe(mockInput); // 未修改state });
2.4 handleAppFocusIn测试
验证有上次聚焦元素时,会自动切回焦点,且最终清空state存储的元素:
it('handleAppFocusIn should focus back saved element if current focus is BODY', () => { jest.useFakeTimers(); const mockInput = document.createElement('input'); mockInput.focus = jest.fn(); // mock focus方法 const wrapper = mount(<MyComponent processAdminMessage={jest.fn()} />, { initialState: { lastFocusedField: mockInput } }); const componentInstance = wrapper.instance(); // 模拟当前焦点在BODY Object.defineProperty(document, 'activeElement', { value: document.body, configurable: true }); componentInstance.handleAppFocusIn(); // 验证state已清空 expect(wrapper.state('lastFocusedField')).toBeNull(); // 执行异步定时器 jest.runAllTimers(); // 验证触发了原元素的focus方法 expect(mockInput.focus).toHaveBeenCalled(); jest.useRealTimers(); });
补充注意项
如果你的组件有componentWillUnmount生命周期做事件解绑,建议同步添加解绑逻辑的测试,方法和绑定测试类似,监听window.removeEventListener的调用即可。
内容的提问来源于stack exchange,提问作者Ivan Safronov
相关产品推荐
相关产品推荐

