Mocha 测试移动端 React 组件时怎么处理触摸事件?

一付娟 阅读 21

我在用 Mocha + JSDOM 测试一个移动端的 React 滑动组件,但模拟 touchstart 事件根本没反应,控制台也不报错,本地真机调试是正常的。是不是 JSDOM 不支持 TouchEvent?

我试过用 fireEvent.touchStart(配合 @testing-library/react),也试过手动创建 TouchEvent,但组件里的 onTouchStart 回调就是不触发。下面是我组件里的一小段代码:

const SwipeBox = () => {
  const handleTouchStart = (e) => {
    console.log('touch started', e.touches[0].clientX);
  };

  return (
    <div
      onTouchStart={handleTouchStart}
      style={{ width: '100%', height: '200px', background: '#eee' }}
    />
  );
};
我来解答 赞 3 收藏
二维码
手机扫码查看
1 条解答
博主凌萓
JSDOM 本身确实不完整支持 TouchEvent,尤其是移动端的 Touch API 是依赖浏览器原生实现的,JSDOM 只能模拟部分鼠标事件,touch 事件默认是空实现,所以你写 onTouchStart 自然不会触发。

但问题关键不在 JSDOM,而在于你用的测试环境和库组合。React 的事件系统会把 touch 事件挂到 DOM 上,但如果你用的是旧版 JSDOM(比如 v16.x 之前),它连 document.createEvent('TouchEvent') 都不支持,或者创建出来没 touches 属性,自然没反应。

最简单的办法是升级到 JSDOM ≥ 22.1.0,然后在测试前注入 polyfill:

// setupTests.js
import { JSDOM } from 'jsdom';

const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
url: 'http://localhost',
pretendToBeVisual: true,
resources: 'usable',
});

global.window = dom.window;
global.document = dom.window.document;
global.Touch = dom.window.Touch;
global.TouchList = dom.window.TouchList;
global.TouchEvent = dom.window.TouchEvent;


然后在 mocha 启动参数里加 --require ./setupTests.js

不过更推荐的是直接用 @testing-library/user-eventuserEvent.pointer,它会自动处理 touch/mouse 的兼容:

import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import SwipeBox from './SwipeBox';

test('triggers onTouchStart', async () => {
const handleTouchStart = jest.fn();
render(<SwipeBox onTouchStart={handleTouchStart} />);
const div = screen.getByRole('generic'); // 或者加个 aria-label 方便定位

const user = userEvent.setup();
await user.pointer({ target: div, keys: '[touchstart]' });

expect(handleTouchStart).toHaveBeenCalled();
});


如果还是不行,你也可以手动构造一个 fake touch 对象,让 React 的事件系统能识别:

const touch = { clientX: 100, clientY: 200, identifier: 1, target: div };
const touchList = { length: 1, item: () => touch, [Symbol.iterator]: function* () { yield touch; } };
const touchEvent = new TouchEvent('touchstart', { touches: touchList, targetTouches: touchList, changedTouches: touchList });
div.dispatchEvent(touchEvent);


注意:上面这段必须在 JSDOM 支持 TouchEvent 的前提下才有效,所以还是得先升级环境。

总之,核心就是:别指望 JSDOM 天生支持 touch,要么升级它,要么用 user-event 的 pointer 模拟,要么自己打补丁。真机没问题是因为手机浏览器有完整的 Touch API,而测试环境得你手动补上。
点赞 5
2026-02-26 17:06