React Testing Library中forEach点击不触发onClick,for循环却可行?
为何forEach遍历点击React组件元素时,Jest Mock回调调用次数为0?
我写了一个React函数组件Shop,它接收onClickMenu回调作为props,渲染成列表后每个列表项的onClick都绑定这个回调。用Jest Mock回调、userEvent做测试时发现:用forEach遍历点击所有元素,测试会失败,显示回调调用次数为0;但换成for循环或者手动逐个点击(加await),测试就能正常通过。
组件代码
function Shop(props) { const { onClickMenu } = props; const tabs = ['All', 'Boards', 'Boots', 'Bindings']; const renderedList = tabs.map((tab, i) => ( <li key={i} onClick={onClickMenu}> {tab} </li> )); return <ul>{renderedList}</ul>; }
失败的测试代码
test('calls onClickMenu correct number of times', async () => { const mockHandler = jest.fn(); const user = userEvent.setup(); render(<Shop onClickMenu={mockHandler} />); const menu = ['All', 'Boards', 'Boots', 'Bindings']; menu.forEach((tab) => { user.click(screen.getByText(tab)); }); expect(mockHandler).toHaveBeenCalledTimes(4); });
测试失败结果
expect(jest.fn()).toHaveBeenCalledTimes(expected) Expected number of calls: 4 Received number of calls: 0
可行的测试代码
// for循环方式 for (let i = 0; i < menu.length; i++) { await user.click(screen.getByText(menu[i])) } // 手动逐个点击方式 await user.click(screen.getByText('All')); await user.click(screen.getByText('Boards')); await user.click(screen.getByText('Boots')); await user.click(screen.getByText('Bindings'));
原因解析
核心问题出在异步操作的处理:
userEvent.click()是异步函数,返回Promise对象,它模拟真实用户点击的完整流程(包括事件冒泡、状态更新等),需要等待其完成才能确保回调被触发。- forEach循环中,直接调用
user.click()但没有加await,会导致所有点击操作都处于pending状态时,就直接执行了后面的expect断言。此时Jest的mock回调还没被任何点击触发,所以调用次数为0。 - 而for循环或手动逐个点击时,用
await等待每个user.click()完成,确保每一次点击的异步流程结束、回调被触发后,再进行下一次操作或断言,因此mock的调用次数能被正确统计。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

