0

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
  • 1
    If you're trying to test the hook then you definitely shouldn't mock it, although it might make sense to mock the hook to test the component. Given that your hook makes a network request I'd be inclined to use e.g. MSW to test it does the right thing. Commented Jul 17, 2024 at 18:38
  • thanks, @jonrsharpe . MSW seems to be the best option indeed, specially becausae I'm using vitest and they suggest in the docs using it when mocking http requests. Commented Jul 18, 2024 at 20:51

2 Answers 2

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);
    });
  });
});
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks, that would definitely work if I was using Jest! But as I'm using vitest, I can't use jest.fn
I think you can just replace jest.fn with vi.fn.
you're right! It worked perfectly! My waitFor implementation was also wrong. Thanks
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.