Raphael40
Raphael40

Reputation: 1

React Vitest mocking api fetch returns: TypeError: Cannot read properties of undefined (reading 'json')

In a personal Vite React project I want to display a list of anime on a page from a Rapid-Api api. I have a displayAnime function in the AnimeInfoPage component (/:id) I am trying to test:


const AnimeInfoPage = () => {
    const [anime, setAnime] = useState({ genres: [] });
    const { id } = useParams();

    const displayAnime = useMemo(
        () => async () => {
            const url = `https://anime-db.p.rapidapi.com/anime/by-id/${id}`;
            const options = {
                method: 'GET',
                headers: {
                    'X-RapidAPI-Key': `${apiKey}`,
                    'X-RapidAPI-Host': 'anime-db.p.rapidapi.com'
                }
            };

            try {
                const response = await fetch(url, options);
                const result = await response.json();
                setAnime(result);
            } catch (error) {
                console.error(error);
            }
        },
        [id]
    );

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

return (...

So the code works and I get what I want on the screen but In my test I am having some trouble.

import { beforeEach, afterEach, describe, it, expect, vi } from 'vitest';
import { render, screen, cleanup } from '@testing-library/react';

import * as matchers from '@testing-library/jest-dom/matchers';
expect.extend(matchers);

import AnimeInfoPage from '.';
import { useParams } from 'react-router-dom';

global.fetch = vi.fn();

vi.mock('react-router-dom', async () => ({
    ...(await vi.importActual('react-router-dom')),
    useParams: vi.fn()
}));

function createFetchResponse(data) {
    return { json: () => new Promise(resolve => resolve(data)) };
}

const apiKey = import.meta.env.VITE_API_KEY;

describe('AnimeInfoPage component', () => {

    beforeEach(() => {
        vi.mocked(useParams).mockReturnValue({ id: '4040' });
        render(<AnimeInfoPage />);
    });

    afterEach(() => {
        cleanup();
    });

    it('renders an anime from the api on the page', async () => {
        const animeData = {
            _id: '4040',
            title: 'Test Anime',
        };

        fetch.mockResolvedValue(createFetchResponse(animeData));

        expect(fetch).toHaveBeenCalledWith(`https://anime-db.p.rapidapi.com/anime/by-id/4040`, {
            method: 'GET',
            headers: {
                'X-RapidAPI-Key': `${apiKey}`,
                'X-RapidAPI-Host': 'anime-db.p.rapidapi.com'
            }
        });

        await screen.findByText(/Test Anime/);
    });
});

In this test the first expect(fetch).toHaveBeenCalledWith passes however the second fails because there is no text 'Test Anime' on the screen. In my console I get a message saying: TypeError: Cannot read properties of undefined (reading 'json'). Turns out if I console.log(response) in the displayAnime function I receive undefined. So I am wondering if anyone can help me figure out why my mocked data isn't being mocked into the resolved value of the fetch.

Tried changing the syntax of createFetchResponse function but no changes

Upvotes: 0

Views: 612

Answers (1)

Raphael40
Raphael40

Reputation: 1

Figured it out myself, turns out that for this test you have to render the component inside the it block instead of doing it beforeEach. Not sure why that is the case for this test and not my others though.

Upvotes: 0

Related Questions