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

Jest测试中如何Mock函数式组件未导出的内部函数?

结论

完全可以实现,且不需要引入任何第三方依赖库,也不需要修改业务组件的对外导出逻辑。

这些事件处理函数定义在组件函数的内部作用域,既没有对外导出也没有挂载到全局对象,Jest无法直接获取到函数引用做替换,不需要硬钻作用域取函数,用下面两种原生支持的方案就能解决:


方案1(推荐):Mock内部函数依赖的底层API,零侵入组件代码

你提到函数里存在和Jest不兼容的指令,本质上绝大多数不兼容问题,都是因为Jest默认搭配的jsdom环境没有实现对应DOM API(比如你示例代码里的canvas.getContext('2d')绘图能力、getBoundingClientRect返回值异常等)。
这种场景根本不需要接触组件内部的setTouchPosition、draw函数,只要在测试用例执行前,把这些函数会调用到的底层DOM API提前mock掉即可,内部函数执行时自然会走你mock的逻辑,不会触发不兼容的原生调用。

对应示例组件的测试前置代码参考:

beforeEach(() => {
  // 构造mock的canvas对象,把内部用到的方法全部替换为jest mock函数
  const mockCanvasCtx = {
    beginPath: jest.fn(),
    arc: jest.fn(),
    lineTo: jest.fn(),
    stroke: jest.fn(),
    fill: jest.fn()
  }
  const mockCanvas = {
    getBoundingClientRect: jest.fn(() => ({ top: 0, left: 0, width: 300, height: 600 })),
    getContext: jest.fn(() => mockCanvasCtx)
  }
  // 重写getElementById,返回我们构造的mock canvas
  document.getElementById = jest.fn((id) => {
    if (id === 'canvas') return mockCanvas
    return document.createElement(id)
  })
})

写完前置逻辑后,正常渲染组件、模拟触发对应的touch、鼠标事件即可,内部函数执行时不会再触发jsdom不兼容的逻辑。这个方案完全不碰业务组件的实现细节,属于前端测试的最佳实践。


方案2:拦截事件绑定逻辑,直接替换事件回调

如果你需要直接断言内部事件函数的调用次数、入参,甚至完全替换内部函数的执行逻辑,可以通过重写事件监听的原型方法实现拦截——毕竟所有内部定义的事件处理函数,最终都会通过addEventListener绑定到对应的DOM节点上。

实现逻辑参考:

test('组件事件逻辑校验', () => {
  // 自定义mock函数
  const mockSetTouchPosition = jest.fn((e) => { e.preventDefault() })
  const mockDraw = jest.fn()
  // 暂存原生的事件绑定方法
  const originAddEventListener = EventTarget.prototype.addEventListener

  // 重写事件绑定方法,拦截目标事件的回调替换
  EventTarget.prototype.addEventListener = jest.fn(function (eventName, handler, options) {
    if (eventName === 'touchstart' || eventName === 'touchmove') {
      // 匹配到目标事件,替换为我们自定义的mock函数
      return originAddEventListener.call(this, eventName, mockSetTouchPosition, options)
    }
    if (eventName === 'mousemove') {
      return originAddEventListener.call(this, eventName, mockDraw, options)
    }
    // 其他事件保持原有逻辑不变
    return originAddEventListener.call(this, eventName, handler, options)
  })

  // 此处正常编写渲染组件、模拟触发事件的测试逻辑
  // 触发事件后就可以直接对mockSetTouchPosition、mockDraw做调用断言

  // 用例执行完记得还原原生方法,避免污染其他测试用例
  afterAll(() => {
    EventTarget.prototype.addEventListener = originAddEventListener
  })
})

这个方案同样只用到Jest自带的mock能力,不需要额外安装依赖。


注意事项

不要为了方便测试把组件内部的工具函数、事件函数提到组件外部导出,这种为了测试修改业务代码结构的做法会提升后续的维护成本,没有必要。

内容的提问来源于stack exchange,提问作者juliano.net

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:18:43