You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jest/RTL中测试mouseOut/取消悬浮事件?

解决Jest/RTL测试mouseOut时currentDocument.elementFromPoint is not a function错误

先修正测试中的基础错误

你的组件里img标签的data-testid是info-icon,但测试代码中用的是screen.getByTestId('tooltip-icon'),这会导致找不到目标元素,事件无法正确触发,先把这个匹配错误修正。


针对elementFromPoint错误的解决方案

这个错误是因为Tooltip组件内部处理mouseOut事件时调用了elementFromPoint方法,但Jest的jsdom环境并未实现该API,以下是三种可行解决办法:

1. 模拟document.elementFromPoint方法

在测试文件顶部或Jest全局setup文件中添加模拟逻辑,让组件认为鼠标已完全离开元素:

beforeAll(() => {
  // 模拟elementFromPoint,返回null表示鼠标不在任何元素范围内
  document.elementFromPoint = jest.fn(() => null);
});

2. 使用@testing-library/user-event替代fireEvent

userEvent提供更贴近真实用户的交互API,能避免原生事件模拟的兼容性问题:
首先安装依赖:

npm install --save-dev @testing-library/user-event

然后修改测试代码:

import userEvent from '@testing-library/user-event';

it('Should test tooltip', async () => {
  const user = userEvent.setup();
  render(<Tooltip />);
  
  // 触发悬浮
  await user.hover(screen.getByTestId('info-icon'));
  // 自动等待元素出现并断言
  expect(await screen.findByText("'Information'")).toBeInTheDocument();
  
  // 取消悬浮
  await user.unhover(screen.getByTestId('info-icon'));
  // 用queryByText判断元素不存在(找不到不会抛出错误)
  expect(screen.queryByText("'Information'")).not.toBeInTheDocument();
});

3. 改用mouseLeave事件替代mouseOut

mouseLeave不会冒泡,可绕过组件中基于elementFromPoint的子元素判断逻辑:

it('Should test tooltip', async () => {
    render(<Tooltip />);
    const icon = screen.getByTestId('info-icon');
    
    fireEvent.mouseOver(icon);
    await waitFor(() => screen.getByTestId('tooltip-info'));
    expect(screen.getByText("'Information'")).toBeInTheDocument();
    
    // 改用mouseLeave事件触发离开逻辑
    fireEvent.mouseLeave(icon);
    await waitForElementToBeRemoved(() => screen.getByTestId('tooltip-info'));
    // 用queryByText断言元素不存在
    expect(screen.queryByText("'Information'")).not.toBeInTheDocument();
});

内容的提问来源于stack exchange,提问作者Sole

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 18:50:26