ln09nv2
ln09nv2

Reputation: 1343

How to mock fetch in custom hook with Mock Service Worker?

I want to test my application that has a user input a query that sends a request to the Google Books API, then the response is displayed.

I've separated the data fetching logic into its own custom hook. I want to mock the fetch with Mock Service Worker. I'm receiving an invalid hook call when I run my test.

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
    1. You might have mismatching versions of React and the renderer (such as React DOM)
    2. You might be breaking the Rules of Hooks
    3. You might have more than one copy of React in the same app
    See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

      3 |
      4 | export const useBooksFetch = (initialUrl, initialData) => {
    > 5 |   const [url, setUrl] = useState(initialUrl);
        |                         ^
      6 |   const [data, setData] = useState(initialData);
      7 |   const [isLoading, setIsLoading] = useState(false);
      8 |   const [isError, setIsError] = useState(false);

      at resolveDispatcher (node_modules/react/cjs/react.development.js:1476:13)
      at useState (node_modules/react/cjs/react.development.js:1507:20)
      at useBooksFetch (src/hooks/useBooksFetch/useBooksFetch.js:5:25)
      at Object.<anonymous> (src/hooks/useBooksFetch/useBooksFetch.test.js:78:24)

  console.warn
    [MSW] Found a redundant usage of query parameters in the request handler URL for "GET https://www.googleapis.com/books/v1/volumes?q=travel". Please match against a path instead, and access query parameters in the response resolver function:
    
    rest.get("/books/v1/volumes", (req, res, ctx) => {
      const query = req.url.searchParams
      const q = query.get("q")
    })      

      4 |
      5 | const server = setupServer(
    > 6 |   rest.get(
        |        ^
      7 |     "https://www.googleapis.com/books/v1/volumes?q=travel",
      8 |     (req, res, ctx) => {
      9 |       return res(

Confirmations

What am I doing wrong? CodeSandbox

// useBooksFetch.js
export const useBooksFetch = (initialUrl, initialData) => {
  const [url, setUrl] = useState(initialUrl);
  const [data, setData] = useState(initialData);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    const fetchBooks = async () => {
      setIsError(false);
      setIsLoading(true);
      try {
        const result = await axios(url);
        setData(result.data);
      } catch (isError) {
        setIsError(true);
      }
      setIsLoading(false);
    };
    fetchBooks();
  }, [url]);

  return [{ data, isLoading, isError }, setUrl];
};


// useBooksFetch.test.js
const server = setupServer(
  rest.get(
    "https://www.googleapis.com/books/v1/volumes?q=travel",
    (req, res, ctx) => {
      return res(
        ctx.status(200),
        ctx.json({
          data: {
            items: [
              {
                volumeInfo: {
                  title: "Travels"
                }
              },
              {
                volumeInfo: {
                  title: "The Travel Book"
                }
              },
              {
                volumeInfo: {
                  title: "Two Arabic Travel Books"
                }
              },
              {
                volumeInfo: {
                  title: "Around India in 80 Trains"
                }
              },
              {
                volumeInfo: {
                  title: "World Travel"
                }
              },
              {
                volumeInfo: {
                  title:
                    "The ‘Book’ of Travels: Genre, Ethnology, and Pilgrimage, 1250-1700"
                }
              },
              {
                volumeInfo: {
                  title: "The Impossible Collection of Chinese Art"
                }
              },
              {
                volumeInfo: {
                  title: "Travel Home"
                }
              },
              {
                volumeInfo: {
                  title: "Maximum City"
                }
              },
              {
                volumeInfo: {
                  title: "The Art of Travel"
                }
              }
            ]
          }
        })
      );
    }
  )
);

beforeAll(() => server.listen());
afterAll(() => server.close());
afterEach(() => server.resetHandlers());

it("fetches successfully", async () => {
  const result = await useBooksFetch();
  expect(result.data).toHaveLength(10);
});

Upvotes: 1

Views: 2847

Answers (1)

Stanislav Sidorov
Stanislav Sidorov

Reputation: 11

Error is right, u can't use hooks outside of the component.

Use for example this library https://github.com/testing-library/react-hooks-testing-library

Upvotes: 1

Related Questions