使用fetch-retry包裹全局fetch时单元测试报‘fetch must be a function’的解决方法
解决fetch-retry单元测试
fetch must be a function报错问题 问题说明
fetch-retry是基于全局fetch封装的重试npm包,使用方式为:
const fetch = require('fetch-retry')(global.fetch)
在运行单元测试时,测试套件抛出fetch must be a function错误,但移除fetch-retry后测试能正常执行。现有未引入fetch-retry的测试代码如下:
import React from 'react'; import { render, screen } from '@testing-library/react'; import Test from "./Test"; describe("Test", () => { let originalFetch; beforeEach(() => { originalFetch = global.fetch; global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ value: "Testing something!" }) })); }); afterEach(() => { global.fetch = originalFetch; }); it('Should have proper description after data fetch', async () => { render(<Test />); const description = await screen.findByTestId('description'); expect(description.textContent).toBe("Testing something!"); }); });
核心原因
fetch-retry初始化时需要传入一个有效的函数类型的fetch,但当前测试中mock全局fetch的时机晚于fetch-retry的初始化,导致fetch-retry拿到的不是有效函数,从而抛出错误。
解决方案
方案1:提前mock全局fetch并初始化fetch-retry
在引入待测试组件前,先完成全局fetch的mock,再初始化fetch-retry,确保组件使用的是包装后的mock fetch:
import React from 'react'; import { render, screen } from '@testing-library/react'; // 提前mock全局fetch global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ value: "Testing something!" }) })); // 用mock的fetch初始化fetch-retry const fetch = require('fetch-retry')(global.fetch); // 引入待测试组件 import Test from "./Test"; describe("Test", () => { let originalFetch; beforeEach(() => { originalFetch = global.fetch; // 重置mock避免测试间污染 global.fetch.mockReset(); global.fetch.mockResolvedValue({ json: () => Promise.resolve({ value: "Testing something!" }) }); }); afterEach(() => { global.fetch = originalFetch; }); it('Should render correct data after fetch', async () => { render(<Test />); const description = await screen.findByTestId('description'); expect(description.textContent).toBe("Testing something!"); // 可选:验证fetch调用次数 expect(global.fetch).toHaveBeenCalledTimes(1); }); });
方案2:直接mock fetch-retry包
如果组件是直接引入fetch-retry使用的,可通过jest.mock直接mock整个包,返回预设的mock函数:
import React from 'react'; import { render, screen } from '@testing-library/react'; import Test from "./Test"; // mock fetch-retry,让它返回自定义的mock fetch jest.mock('fetch-retry', () => { return () => jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ value: "Testing something!" }) })); }); describe("Test", () => { let mockFetch; beforeEach(() => { mockFetch = require('fetch-retry')(); mockFetch.mockReset(); mockFetch.mockResolvedValue({ json: () => Promise.resolve({ value: "Testing something!" }) }); }); it('Should display correct data after fetch', async () => { render(<Test />); const description = await screen.findByTestId('description'); expect(description.textContent).toBe("Testing something!"); expect(mockFetch).toHaveBeenCalledTimes(1); }); });
方案3:测试重试逻辑(可选)
若需要验证fetch-retry的重试行为,可让mock fetch先失败再成功,验证调用次数:
import React from 'react'; import { render, screen } from '@testing-library/react'; // 配置mock fetch:第一次失败,第二次成功 global.fetch = jest.fn() .mockRejectedValueOnce(new Error('Fetch failed')) .mockResolvedValue({ json: () => Promise.resolve({ value: "Testing something!" }) }); // 初始化fetch-retry const fetch = require('fetch-retry')(global.fetch); import Test from "./Test"; describe("Test", () => { beforeEach(() => { global.fetch.mockReset(); global.fetch .mockRejectedValueOnce(new Error('Fetch failed')) .mockResolvedValue({ json: () => Promise.resolve({ value: "Testing something!" }) }); }); it('Should retry fetch on failure and render data', async () => { render(<Test />); const description = await screen.findByTestId('description'); expect(description.textContent).toBe("Testing something!"); // 验证fetch被调用2次(失败重试一次) expect(global.fetch).toHaveBeenCalledTimes(2); }); });
内容的提问来源于stack exchange,提问作者Taranjit Kang
相关产品推荐
相关产品推荐

