如何测试useEffect内的updateCurrentTitle滚动逻辑?Jest/RTL遇阻
问题描述
我正在使用Jest和React Testing Library测试组件,当前代码通过滚动位置更新TopBar组件中的标题,组件核心逻辑如下:
const [currentTitle, setTitle] = useState<string>(defaultProps.collectionName) useEffect(() => { const collections = document.getElementsByClassName('cc-look-collection') const childNodes = collections[0].childNodes const updateCurrentTitle = () => { const currentPosition = window.scrollY let accumulatedHeight = 0 for (let i = 0; i < childNodes.length; i++) { const collection = childNodes[i] const collectionHeight = (collection as HTMLInputElement)?.offsetHeight accumulatedHeight += collectionHeight if (currentPosition < accumulatedHeight) { const title = collection.childNodes[0].textContent! setTitle(title) break } } } window.addEventListener('scroll', updateCurrentTitle) }, [collections])
我想测试updateCurrentTitle函数,但测试无法正常运行,模拟滚动事件后标题没有更新。我的测试用例如下:
it('should update TopBar collectionName based on scroll position', () => { // Given const props = lookCollectionProps const { container } = render(<LookCollection {...props} />) // eslint-disable-next-line testing-library/no-node-access const content = container.firstChild as HTMLElement jest.useFakeTimers() window.dispatchEvent(new Event('scroll')) const baseText = within(content).getByText('Collection n°1') expect(baseText).toBeInTheDocument() act(() => { jest.advanceTimersByTime(1500) }) const expectedText = within(content).getByText('Collection n°2') expect(expectedText).toBeInTheDocument() })
运行测试时遇到错误:Unable to find an element with the text: Collection n°2。
解决方案
你的测试和组件逻辑存在几个关键问题,逐一修复即可:
修复useEffect依赖问题
document.getElementsByClassName返回的是动态HTMLCollection,每次渲染都会生成新引用,导致useEffect无限触发。建议把DOM查询逻辑移到updateCurrentTitle内部,避免依赖动态集合,同时加上清理函数移除监听:useEffect(() => { const updateCurrentTitle = () => { const collections = document.getElementsByClassName('cc-look-collection') if (!collections[0]) return; const childNodes = collections[0].childNodes const currentPosition = window.scrollY let accumulatedHeight = 0 for (let i = 0; i < childNodes.length; i++) { const collection = childNodes[i] as HTMLElement const collectionHeight = collection.offsetHeight if (!collectionHeight) continue; accumulatedHeight += collectionHeight if (currentPosition < accumulatedHeight) { const title = collection.childNodes[0].textContent! setTitle(title) break } } } window.addEventListener('scroll', updateCurrentTitle) return () => window.removeEventListener('scroll', updateCurrentTitle) }, [])模拟正确的滚动位置
测试中仅触发scroll事件但未修改window.scrollY,滚动位置始终为0,只会匹配第一个集合。需要先计算滚动到第二个集合的位置,再模拟滚动并触发事件:it('should update TopBar collectionName based on scroll position', () => { const props = lookCollectionProps const { container } = render(<LookCollection {...props} />) // 获取第一个集合的高度,计算滚动位置 // eslint-disable-next-line testing-library/no-node-access const firstCollection = container.querySelector('.cc-look-collection > *') as HTMLElement const scrollToPosition = firstCollection.offsetHeight + 10 // 模拟滚动到指定位置 window.scrollTo(0, scrollToPosition) window.dispatchEvent(new Event('scroll')) // 验证标题更新 expect(within(container).getByText('Collection n°2')).toBeInTheDocument() })移除无用的fake timers
组件逻辑中没有定时器,测试里的jest.useFakeTimers()和advanceTimersByTime完全多余,直接删除即可。修正类型断言
组件中将collection断言为HTMLInputElement不合理,改为HTMLElement,避免类型错误导致offsetHeight获取失败。
内容的提问来源于stack exchange,提问作者Kimimo
相关产品推荐
相关产品推荐

