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

React自定义文件下载Hook测试:window.URL.mock无效问题排查

如何正确测试React自定义Hook useFileDownload?

我开发了一个用于从URL下载文件的React自定义Hook useFileDownload,功能运行正常,目前正在对其行为进行测试。

Hook代码

const useFileDownload = ({ apiResponse, fileName }) => {
  const ref = useRef(null)
  const [url, setUrl] = useState('')
  const [name, setName] = useState('')

  const download = async () => {
    const { data } = await apiResponse()
    const objectUrl = window.URL.createObjectURL(new Blob([data]))

    setUrl(objectUrl)
    setName(fileName)

    ref.current?.click()
    window.URL.revokeObjectURL(objectUrl)
  }

  return { url, ref, name, download }
}

组件中调用Hook的方式

const { ref, url, download, name } = useFileDownload({
  apiResponse: () => axios.get(pdfUrl, { responseType: 'blob' }),
  fileName: 'my_custom.pdf'
})

// 组件中隐藏的a标签
<a href={url} download={name} ref={ref} />

当前测试代码

describe('useFileDownload', () => {
  it('should create refs', async () => {
    window.URL.createObjectURL = jest.fn()
    const mockRequest = jest.fn().mockResolvedValue({ data: 'url/to/pdf' })
    const { result } = renderHook(() => useFileDownload({ apiResponse: mockRequest, fileName: 'sample.pdf' }))

    act(() => {
      result.current.download()
    })

    expect(result.current.name).toBe('sample.pdf')
    expect(mockRequest).toBeCalledTimes(1)
  })
})

我尝试mock window.URL.createObjectURL但似乎并未生效,不确定这种mock方式是否正确。如果window.URL相关代码执行失败,后续断言也会跟着失败,请问该如何正确测试这个Hook?


解决方案

1. 正确mock window.URL相关方法

直接赋值window.URL.createObjectURL的方式在jest中可能不会被Hook正确捕获,建议用jest.spyOn创建mock,同时mockrevokeObjectURL避免内存泄漏警告:

// 测试前初始化mock
beforeEach(() => {
  jest.spyOn(window.URL, 'createObjectURL').mockReturnValue('mock-object-url')
  jest.spyOn(window.URL, 'revokeObjectURL').mockImplementation(() => {})
})

// 测试后恢复原始方法
afterEach(() => {
  jest.restoreAllMocks()
})

2. 处理异步的download函数

download是异步函数,直接用act包裹不会等待异步操作完成,需要配合async/await和waitFor(来自@testing-library/react)等待状态更新:

it('should handle download correctly', async () => {
  const mockRequest = jest.fn().mockResolvedValue({ data: new Blob(['test content']) })
  const { result, waitFor } = renderHook(() => useFileDownload({ apiResponse: mockRequest, fileName: 'sample.pdf' }))

  // 模拟ref的current对象包含click方法
  const mockClick = jest.fn()
  result.current.ref.current = { click: mockClick }

  // 调用异步download函数
  await result.current.download()

  // 等待状态更新完成后断言
  await waitFor(() => {
    expect(result.current.name).toBe('sample.pdf')
    expect(result.current.url).toBe('mock-object-url')
  })

  // 验证核心逻辑
  expect(mockRequest).toBeCalledTimes(1)
  expect(mockClick).toBeCalledTimes(1)
  expect(window.URL.revokeObjectURL).toBeCalledWith('mock-object-url')
})

3. 模拟Blob对象(可选)

如果测试环境对Blob存在兼容性问题,可以提前mockBlob构造函数:

beforeEach(() => {
  global.Blob = jest.fn().mockReturnValue({})
})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 04:15:49