如何在Jest中测试React项目里Mousetrap创建的事件
我之前也碰到过一模一样的问题!Mousetrap默认会把键盘事件绑定到window对象上,你用的simulant或者ReactTestUtils的模拟方式,要么没正确设置事件的关键属性,要么没让事件正确冒泡到window,所以触发不了Mousetrap的回调。下面给你两种可靠的解决方法:
方案一:模拟符合Mousetrap要求的原生键盘事件
Mousetrap在处理keydown事件时,会检查事件的keyCode/which,并且要求事件能冒泡到window。直接创建原生的KeyboardEvent并通过window.dispatchEvent触发,就能被Mousetrap捕获到:
it("checks on left arrow press triggers onLeftClick", () => { const spy = jest.spyOn(MyComponent.prototype, "onLeftClick"); const wrapper = mount(<MyComponent />); // 创建符合Mousetrap期望的键盘事件,必须设置bubbles为true const leftArrowEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft', keyCode: 37, which: 37, bubbles: true, cancelable: true }); // 直接在window上触发事件 window.dispatchEvent(leftArrowEvent); expect(spy).toHaveBeenCalled(); spy.mockReset(); spy.mockRestore(); });
为什么之前的方法无效?因为simulant或者ReactTestUtils.simulate创建的事件可能没有默认开启bubbles属性,而Mousetrap绑定在window上,事件必须冒泡到这个层级才能被监听。
方案二:Mock Mousetrap库(更稳定的测试方式)
如果你的测试重点是验证组件是否正确绑定了Mousetrap事件,而不是模拟真实的键盘输入流程,直接Mock Mousetrap会更简洁可靠,还能避免依赖事件模拟的细节:
首先在测试文件顶部Mock Mousetrap:
// 全局Mock Mousetrap的bind方法 jest.mock('mousetrap', () => ({ bind: jest.fn(), unbind: jest.fn() // 如果组件用到unbind也可以加上 })); import Mousetrap from 'mousetrap';
然后编写测试用例:
it("binds left arrow key to onLeftClick method", () => { const spy = jest.spyOn(MyComponent.prototype, "onLeftClick"); mount(<MyComponent />); // 验证Mousetrap.bind是否被正确调用,参数为左箭头键和组件的回调 expect(Mousetrap.bind).toHaveBeenCalledWith('left', expect.any(Function)); // 取出绑定的回调函数并手动调用,验证组件方法是否触发 const [_, callback] = Mousetrap.bind.mock.calls[0]; callback(); expect(spy).toHaveBeenCalled(); spy.mockReset(); spy.mockRestore(); });
这种方法的好处是:不需要关心Mousetrap内部如何处理事件,只需要验证组件是否正确和Mousetrap完成了绑定逻辑,测试更稳定,即使Mousetrap的内部实现更新,你的测试也不会轻易失效。
额外提醒(针对函数组件)
如果你的MyComponent是函数组件,不能直接spyOn原型方法,需要把onLeftClick作为props传入,或者在组件内部用jest.fn()创建mock函数:
it("triggers onLeftClick on left arrow press", () => { const mockOnLeftClick = jest.fn(); const wrapper = mount(<MyComponent onLeftClick={mockOnLeftClick} />); const leftArrowEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft', keyCode: 37, which: 37, bubbles: true }); window.dispatchEvent(leftArrowEvent); expect(mockOnLeftClick).toHaveBeenCalled(); });
内容的提问来源于stack exchange,提问作者prasadmsvs

