Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
414 views
in Technique[技术] by (71.8m points)

javascript - Mocking fetch with jest.fn() in React

I'm wondering why I need to put fetch mock logic inside my test to make it work.

Here is simple example:

Component to test with fetch inside useEffect and state update after response:

// Test.jsx

import React, {useEffect, useState} from 'react'

export const Test = () => {

    const [description, setDescription] = useState<string | null>(null)

    const fetchData = async () => {
        const response = await fetch('https://dummyendpoint/');
        const parsed = await response.json();
        const description = parsed.value;
        setDescription(description);
    }

    useEffect(() => {
        fetchData();
    }, [])

    return (
        <div data-testid="description">
            {description}
        </div>
    )
};

export default Test;

Test logic:

// Test.test.js

import React from 'react';
import {render, screen} from '@testing-library/react';

import Test from "./Test";

global.fetch = jest.fn(() => Promise.resolve({
    json: () => Promise.resolve({
        value: "Testing something!"
    })
}));

describe("Test", () => {

    it('Should have proper description after data fetch', async () => {

        // need to put mock logic here to make it work

        render(<Test/>);
        const description = await screen.findByTestId('description');
        expect(description.textContent).toBe("Testing something!");
    });
})

If I keep global.fetch mock at the top of my test file, I keep getting an error:

TypeError: Cannot read property 'json' of undefined
at const parsed = await response.json();
question from:https://stackoverflow.com/questions/65946740/mocking-fetch-with-jest-fn-in-react

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It's really strange that it does not work as it is.

But I was able to fix it by moving the setup into beforeEach block (I assume beforeAll would also work).

It is a common pattern to backup global variable value, override it for tests and restore it back.

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 () => {

        // need to put mock logic here to make it work

        render(<Test />);
        const description = await screen.findByTestId('description');
        expect(description.textContent).toBe("Testing something!");
    });
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...