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

如何使用Jest/RTL mock函数组件ref属性覆盖测试边界分支

可行测试方案

你之前mock useRef 不生效的核心原因:React在DOM节点挂载完成后,会自动给传入的ref对象的current属性赋值为对应的DOM实例,如果你只是简单mock useRef 返回{current: null},等<img>挂载完成后ref.current还是会被React自动改成img节点,自然走不到空值判断分支。

以下是三种可落地的方案,优先推荐第一种:

方案1:抽离辅助函数单独测试(最推荐)

helperFunction本身是独立的逻辑判断,完全不需要依赖组件渲染流程来测试,直接把它从组件内部抽离导出,单独传参测试即可100%覆盖分支:

// 组件文件里把helperFunction导出
export function helperFunction(node, ref) { 
  if (!ref || !ref.current) return; 
  // ...do something    
}

export default function Component() {
  const imgRef = useRef(null)     
  return (
    <AlignComponent onAlign={(node) => {helperFunction(node, imgRef)}}>
      <img ref={imgRef} />
    </AlignComponent>
  )
}

测试代码直接写:

import { helperFunction } from './component-path'

test('helperFunction should early return when ref is invalid', () => {
  const mockNode = document.createElement('div')
  // 覆盖!ref分支
  expect(helperFunction(mockNode, null)).toBeUndefined()
  // 覆盖!ref.current分支
  expect(helperFunction(mockNode, { current: null })).toBeUndefined()
})

这个方案没有任何hack逻辑,完全符合单元测试的隔离原则,不会和React的内部ref赋值机制冲突,覆盖率统计完全准确。


方案2:拦截onAlign回调,手动清空ref后触发

如果你不想改动组件代码抽离函数,可以在测试环境拦截子组件的onAlign回调,手动把ref的current值置空后再执行回调,就能走到空值分支。
以React Testing Library + Jest为例:

// 先mock代码里嵌套的那个触发onAlign的组件,拿到onAlign回调的引用
jest.mock('./align-component', () => ({
  __esModule: true,
  default: ({ children, onAlign }) => {
    // 把回调挂到全局,测试里可以手动触发
    globalThis.testTriggerAlign = onAlign
    return <div>{children}</div>
  }
}))

test('cover ref empty branch in component flow', () => {
  render(<Component />)
  // 拿到img对应的ref对象,手动把current置空,模拟ref未赋值的场景
  const imgEl = screen.getByRole('img')
  imgEl.ref.current = null
  // 触发onAlign回调
  globalThis.testTriggerAlign(document.createElement('div'))
  // 后续可以加断言,验证空ref时后续逻辑没有执行
})

方案3:劫持useRef返回值,拦截current赋值

你之前mock useRef失效是因为没有拦截current属性的写入操作,可以通过属性劫持让ref的current永远保持初始null值,React挂载DOM时的赋值操作会直接被拦截:

const mockUseRef = (initialVal) => {
  const ref = {}
  Object.defineProperty(ref, 'current', {
    get: () => initialVal,
    set: () => { /* 拦截所有赋值操作,current始终为初始null */ },
    configurable: true
  })
  return ref
}

// 在测试文件里mock react的useRef
jest.mock('react', () => ({
  ...jest.requireActual('react'),
  useRef: mockUseRef
}))

注意这个方案会污染当前测试文件里所有用到useRef的逻辑,非必要不推荐使用,很容易导致其他测试用例异常。


注意:不要尝试直接给<img>传ref={null}来覆盖分支,React内部会对传入的ref做类型判断,如果ref不是对象或函数,会直接跳过ref挂载流程,这时候你传入组件的imgRef本身还是正常的ref对象,只是没有被React绑定到img上,和你要模拟的ref.current === null场景不完全一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 23:51:14