自定义useFetch Hook单元测试出现Object.dispatchError问题求助
自定义useFetch Hook单元测试报错Object.dispatchError的问题
问题背景
运行自定义useFetch Hook的单元测试时,遇到Object.dispatchError错误,相关代码及报错信息如下:
Hook代码
import { useState, useEffect } from 'react'; const useFetch = (url) => { const [data, setData] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); useEffect(() => { setLoading(true); const apiCall = async () => { try { const res = await fetch(url); if (!res.ok) throw new Error(`Error - ${res.status}`); const json = await res.json(); setData(json); setError(null); } catch (err) { setError(`useFetch Error!`); } finally { setLoading(false); } }; apiCall(); }, [url]); return { data, loading, error }; }; export default useFetch;
单元测试代码
import { renderHook, waitFor } from "@testing-library/react"; import useFetch from './useFetch'; it('Should handle no URL provided', async () => { const { result } = renderHook(() => useFetch()); await waitFor(() => { expect(result.current.error).toBe("useFetch Error!"); }); });
报错信息
console.error Error: AggregateError at Object.dispatchError (jsdom\living\xhr\xhr-utils.js:63:19) at Request.<anonymous> (jsdom\lib\jsdom\living\xhr\XMLHttpRequest-impl.js:655:18) at Request.emit (node:events:531:35) at ClientRequest.<anonymous> (jsdom\lib\jsdom\living\helpers\http-request.js:121:14) at ClientRequest.emit (node:events:519:28) at Socket.socketErrorListener (node:_http_client:492:9) at Socket.emit (node:events:519:28) at emitErrorNT (node:internal/streams/destroy:169:8) at emitErrorCloseNT (node:internal/streams/destroy:128:3) at processTicksAndRejections (node:internal/process/task_queues:82:21) undefined
怀疑单元测试中waitFor写法存在问题,想请教如何正确对该自定义Hook进行单元测试?
解决方案
1. 错误根源分析
报错核心并非waitFor写法问题,而是未传入URL时,fetch调用无效地址触发底层网络错误,jsdom无法处理这类无效请求,抛出AggregateError。单元测试的核心原则是隔离外部依赖,不能依赖真实网络请求,必须mockfetch函数。
2. 正确的单元测试写法
通过mock全局fetch函数,模拟不同请求场景,验证Hook的行为:
修改后的错误分支测试代码
import { renderHook, waitFor } from "@testing-library/react"; import useFetch from './useFetch'; // Mock全局fetch函数 global.fetch = jest.fn(); it('Should handle no URL provided', async () => { // 模拟fetch调用失败 fetch.mockRejectedValueOnce(new Error('Invalid URL')); const { result } = renderHook(() => useFetch()); await waitFor(() => { expect(result.current.error).toBe("useFetch Error!"); expect(result.current.loading).toBe(false); }); // 验证fetch被调用一次 expect(fetch).toHaveBeenCalledTimes(1); });
3. Hook健壮性优化
为避免空URL触发无效请求,可在Hook内提前做参数校验:
const useFetch = (url) => { const [data, setData] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); useEffect(() => { // 新增URL校验,提前终止逻辑 if (!url) { setError(`useFetch Error!`); setLoading(false); return; } setLoading(true); const apiCall = async () => { try { const res = await fetch(url); if (!res.ok) throw new Error(`Error - ${res.status}`); const json = await res.json(); setData(json); setError(null); } catch (err) { setError(`useFetch Error!`); } finally { setLoading(false); } }; apiCall(); }, [url]); return { data, loading, error }; };
对应的空URL分支测试可简化为:
it('Should handle empty URL immediately', () => { const { result } = renderHook(() => useFetch()); // 无需waitFor,直接断言 expect(result.current.error).toBe("useFetch Error!"); expect(result.current.loading).toBe(false); // 验证fetch未被调用 expect(fetch).not.toHaveBeenCalled(); });
4. 其他测试场景补充
除错误分支,还需覆盖成功加载、加载中、HTTP错误状态等场景:
// 测试成功加载 it('Should fetch data successfully', async () => { const mockData = { id: 1, name: 'Test Data' }; fetch.mockResolvedValueOnce({ ok: true, json: async () => mockData, }); const { result } = renderHook(() => useFetch('https://example.com/api')); // 校验加载中状态 expect(result.current.loading).toBe(true); await waitFor(() => { expect(result.current.loading).toBe(false); expect(result.current.data).toEqual(mockData); expect(result.current.error).toBe(null); }); expect(fetch).toHaveBeenCalledWith('https://example.com/api'); }); // 测试HTTP错误状态 it('Should handle HTTP error status', async () => { fetch.mockResolvedValueOnce({ ok: false, status: 404, }); const { result } = renderHook(() => useFetch('https://example.com/api')); await waitFor(() => { expect(result.current.error).toBe("useFetch Error!"); expect(result.current.loading).toBe(false); }); });
内容的提问来源于stack exchange,提问作者Scottish Smile
相关产品推荐
相关产品推荐

