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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 17:37:07