skip to Main Content

I have a custom hook (useFetch), which I’m using inside a page to pass data into a <Products/> component.

export default function App() {


const { products, totalProducts, currentPage, handleNextPage } = useFetch();  
  
....

<div>{products && <Products products={products}/> }</div>

The problem arrives when I try to use this hook inside a test file to test if Products are being rendered.

This is my custom hook:

import React, { useState, useEffect } from "react";
const useFetch = () => {
  const [products, setProducts] = useState([]);
  const [totalProducts, setTotalProducts] = useState(0);
  const [currentPage, setCurrentPage] = useState(1);
  const limit = 30;

  const fetchData = async (start) => {
    const res = await fetch(
      `https://dummyjson.com/products?skip=${start}&limit=${limit}`,
    );
    const data = await res.json();
    setProducts(data.products);
    setTotalProducts(data.total);
  };

  useEffect(() => {
    fetchData((currentPage - 1) * limit);
  }, [currentPage]);

  const handleNextPage = (page) => {
    setCurrentPage(page);
  };

  return {
    products,
    totalProducts,
    handleNextPage,
    currentPage,
  };
};

export { useFetch };

And this is my test file, which I’m currently only trying to get the populated array of data, but I’m always getting an empty array:

import { renderHook } from "@testing-library/react";
import Products from "./Products";
import { expect } from "vitest";
import { useFetch } from "../../hooks/useFetch";

describe("Products component", () => {
  test("render 30 first products correctly", () => {
    const { result } = renderHook(() => useFetch());

    if (result.current.products) {
      expect(result.current.products.length).toBe(30);
    }
  });
});

What should I do to implement this custom hook? Should I mock it somehow?

I tried to use waitFor method in this way, but still not getting the fetched result data :

await waitFor(() => {
    //   expect(screen.getByText("ID")).toBeInTheDocument();
    // });

2

Answers


  1. you don’t want to make HTTP requests within your tests. instead, you should mock the response from your custom hook. something like this could work:

    import { renderHook } from "@testing-library/react-hooks";
    import { waitFor } from "@testing-library/react";
    import { useFetch } from "../../hooks/useFetch";
    
    global.fetch = jest.fn(() =>
      Promise.resolve({
        json: () => Promise.resolve({
          products: Array.from({ length: 30 }, (_, i) => ({ id: i + 1, name: `Product ${i + 1}` })),
          total: 100,
        }),
      })
    );
    
    describe("useFetch hook", () => {
      test("fetches and sets products correctly", async () => {
        const { result } = renderHook(() => useFetch());
    
        await waitFor(() => {
          expect(result.current.products.length).toBe(30);
        });
      });
    });
    
    Login or Signup to reply.
  2. You can mock the file itself:

    vi.mock('hooks/useFetch.tsx', () => ({
      useFetch: vi.fn().mockReturnValue({
        products: ['product1', 'product2'],
        totalProducts: 2,
        handleNextPage: vi.fn(),
        currentPage: vi.fn(),
      }),
    }));
    

    Most likely you have to replace 'hooks/useFetch.tsx' with your hook’s relative path.

    There is also a way to use msw and server.use() to mock a request from a certain URL itself.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search