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
相关产品推荐
相关产品推荐

