Search code examples
reactjsaxiosreact-hooksreact-testing-libraryreact-hooks-testing-library

Can't mock useAsync with react test library


I am attempting to test a component that uses the useAsync hook using the @testing-library/react .

If I use jest.mock on the TestAPI module, followed by getTest.mockResolvedValueOnce(testArray); on the getTest function then I would expect the mock to correctly return the test values.

Test :

import React from "react";
import { render, screen, waitFor } from "@testing-library/react";
import TestPanel from "./TestPanel";
import { getTest } from "./TestAPI";

jest.mock("./TestAPI");

it("renders cards correctly", async () => {
  const testArray = ["hi there"];
  getTest.mockResolvedValueOnce(testArray);

  render(<TestPanel />);

  expect(getTest).toHaveBeenCalledTimes(1);
  expect(getTest).toHaveBeenCalledWith();

  await waitFor(() => expect(screen.getByText("hi there")).toBeInTheDocument());
});

Component to be tested :

import React from "react";
import { useAsync } from "react-async-hook";
import { getTest } from "./TestAPI";

export default function TestPanel() {
  const { result: elems } = useAsync(getTest, []);

  return (
    <div>
      {elems &&
        elems.map((elem) => {
          return <div>{elem}</div>;
        })}
    </div>
  );
}

API call :

import React from "react";
import axios from "axios";

export async function getTest(): Promise {
  const response = await axios.get("/someservice");

  return response.data || [];
}

However if I run the test then an exception is thrown saying that the "elems.map" is undefined.

On closer inspection it seems that the elems is a promise.

enter image description here

Is there something that I am doing wrong?


Solution

  • These are your issues: react-async-hook & jest

    Here react-async-hook uses instanceof Promise but Jest.mockResolvedValueOnce returns not a real JS Promise, but a promise-like object. So the useAsync() treats the mock as a sycn function.

    The solution is to use mockImplementationOnce instead of mockResolvedValueOnce

    getTest.mockImplementation(async () => testArray)
    

    From the docs:

    mockFn.mockResolvedValueOnce(value)

    Syntactic sugar function for:

    jest.fn().mockImplementationOnce(() => Promise.resolve(value));
    

    But, sadly, it's not just a syntactic sugar.