Jest滚动事件测试未触发回调问题排查与正确测试方法咨询
问题:ScrollWatcher组件Jest测试无法触发底部回调
我开发了ScrollWatcher.tsx组件,代码如下:
import { useEffect } from 'react'; interface Props { onReachBottom: () => void; } export const ScrollWatcher = ({ onReachBottom }: Props) => { useEffect(() => { const handleScroll = () => { if ( window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight ) { onReachBottom(); } }; window.addEventListener('scroll', handleScroll); return () => { window.removeEventListener('scroll', handleScroll); }; }, [onReachBottom]); return <div />; };
该组件监听window滚动事件,滚动到页面底部时触发传入的onReachBottom回调,实际页面中功能正常,但编写的Jest测试用例无法触发该回调。
测试代码如下:
import '@testing-library/jest-dom'; import { fireEvent, render } from '@testing-library/react'; import { ScrollWatcher } from './ScrollWatcher'; describe('ScrollWatcher', () => { it('should call the onReachBottom function when the user scrolls to the bottom of the page', () => { const onReachBottom = jest.fn(); const { container } = render(<ScrollWatcher onReachBottom={onReachBottom} />); const scrollableContainer = container.parentElement; fireEvent.scroll(scrollableContainer, { target: { scrollingElement: { scrollTop: 100 } } }); expect(onReachBottom).not.toHaveBeenCalled(); fireEvent.scroll(scrollableContainer, { target: { scrollingElement: { scrollTop: scrollableContainer.scrollHeight } }, }); expect(onReachBottom).toHaveBeenCalled(); }); });
测试报错信息:
expect(jest.fn()).toHaveBeenCalled() Expected number of calls: >= 1 Received number of calls: 0
请问我的测试用例是否编写错误?正确的测试方式是什么?
解答
测试用例的错误点
- 事件目标不匹配:你的组件监听的是
window的滚动事件,但测试中却给container.parentElement触发滚动,两者不是同一个目标,组件的handleScroll不会被执行。 - 未模拟判断条件所需的DOM属性:组件判断滚动到底部的逻辑依赖
window.innerHeight、document.documentElement.scrollTop和document.documentElement.offsetHeight,测试中没有设置这些属性的正确值,即使触发滚动,判断条件也不成立。
正确的测试代码
直接模拟window的滚动事件,并修改对应DOM属性让判断条件成立:
import '@testing-library/jest-dom'; import { fireEvent, render } from '@testing-library/react'; import { ScrollWatcher } from './ScrollWatcher'; describe('ScrollWatcher', () => { it('triggers onReachBottom when scrolled to page bottom', () => { const onReachBottom = jest.fn(); // 模拟页面高度和窗口高度 Object.defineProperty(document.documentElement, 'offsetHeight', { value: 1000 }); Object.defineProperty(window, 'innerHeight', { value: 500 }); render(<ScrollWatcher onReachBottom={onReachBottom} />); // 滚动未到底部时,回调不触发 Object.defineProperty(document.documentElement, 'scrollTop', { value: 300 }); fireEvent.scroll(window); expect(onReachBottom).not.toHaveBeenCalled(); // 滚动到底部时(window.innerHeight + scrollTop = offsetHeight),回调触发 Object.defineProperty(document.documentElement, 'scrollTop', { value: 500 }); fireEvent.scroll(window); expect(onReachBottom).toHaveBeenCalled(); }); });
关键说明
- 使用
Object.defineProperty修改只读DOM属性:document.documentElement和window的部分属性是只读的,需要用这个方法在测试中模拟它们的值。 - 保持事件目标一致:组件监听
window滚动,测试中就要触发window的滚动事件。 - 严格匹配判断条件:确保
window.innerHeight + document.documentElement.scrollTop等于document.documentElement.offsetHeight,这样组件的判断逻辑才会生效。
内容的提问来源于stack exchange,提问作者Stupid.Fat.Cat
相关产品推荐
相关产品推荐

