React-Query二进制文件上传Hook单元测试失败问题排查
问题描述
我有一个用于向端点上传二进制文件的React-Query Hook,该Hook功能正常,但对应的单元测试却失败了。报错信息显示waitFor超时1000ms,且为网络错误。
Hook代码
import { useMutation, useQueryClient } from 'react-query'; import { IUploadFieldRequest } from '../constants/types'; import { licenseServiceAPI } from '../services'; export const URI = () => '/licenses/upload'; export const useUploadLicense = () => { const queryClient = useQueryClient(); const [apiService] = licenseServiceAPI<IUploadFieldRequest>({ headers: { 'Content-Type': 'multipart/form-data' } }); const { data: response, mutateAsync, isLoading, isSuccess } = useMutation( (formData: IUploadFieldRequest) => { const form = new FormData(); Array.from(formData.files)?.forEach((file) => { form.append('license-file', file); }); return apiService.post(URI(), form); }, { onError: () => { return queryClient.invalidateQueries('licenseUpload'); } } ); return { data: response?.data, uploadFiles: mutateAsync, isLoading, isSuccess }; }; export default useUploadLicense;
测试代码
import { act, renderHook } from '@testing-library/react-hooks'; import { queryClientInstance } from '@oam/shared/test-utils'; import useUploadLicense from './use-upload-license'; const file = new File(['file'], 'license-file.json', { type: 'multipart/form-data' }); it('upload files', async () => { const { result, waitFor } = renderHook(() => useUploadLicense(), { wrapper: queryClientInstance }); act(() => { result.current.uploadFiles({ files: [file] }); }); await waitFor(() => expect(result.current.isLoading).toBeTruthy()); await waitFor(() => expect(result.current.isSuccess).toBeTruthy()); });
接口定义
type IUploadFieldRequest = { files: File[]; };
服务代码
import { AxiosRequestConfig } from 'axios'; import { requestConfig } from '@oam/shared/services'; import init, { Response } from '@oam/shared/utils/request-wrapper'; export const BASE_PATH = process.env.NODE_ENV === 'development' ? '/myurl/v1' : 'http://localhost:7999/v1'; export const licenseServiceAPI = <TRequest, TResponse = TRequest>( config?: AxiosRequestConfig ): [Response<TRequest, TResponse>, AxiosRequestConfig] => { const newConfig = requestConfig({ baseURL: BASE_PATH, headers: { Accept: 'application/json', ...config?.headers }, responseType: config?.responseType ? config?.responseType : undefined }); return [init(newConfig), newConfig]; }; export default licenseServiceAPI;
解决办法
1. Mock API请求(核心解决网络错误)
单元测试不能发送真实网络请求,必须mock掉licenseServiceAPI的post方法,让它返回模拟的成功响应:
在测试文件顶部添加mock代码:
// Mock 整个license服务模块 jest.mock('../services', () => ({ licenseServiceAPI: jest.fn(() => [ { // 模拟post方法返回成功Promise post: jest.fn(() => Promise.resolve({ data: '上传成功' })) }, {} ]) }));
2. 修正测试中的异步逻辑
uploadFiles是异步函数,不能用act同步包裹调用,正确做法是直接await该方法,或结合waitFor跟踪状态:
修改后的测试用例:
it('upload files', async () => { const { result, waitFor } = renderHook(() => useUploadLicense(), { wrapper: queryClientInstance }); // 触发上传并等待异步操作启动 const uploadPromise = result.current.uploadFiles({ files: [file] }); // 验证加载状态 await waitFor(() => expect(result.current.isLoading).toBe(true)); // 等待上传完成 await uploadPromise; // 验证成功状态和返回数据 await waitFor(() => { expect(result.current.isSuccess).toBe(true); expect(result.current.data).toBe('上传成功'); }); });
3. 调整waitFor超时时间(可选)
如果mock后的异步操作仍有超时风险,可以给waitFor指定更长的超时时间:
await waitFor(() => expect(result.current.isSuccess).toBe(true), { timeout: 3000 });
4. 检查测试环境的BASE_PATH
确保测试时BASE_PATH不会指向真实服务器,可在测试前覆盖环境变量:
beforeAll(() => { process.env.NODE_ENV = 'test'; });
内容的提问来源于stack exchange,提问作者Leo Messi
相关产品推荐
相关产品推荐

