Oliver Watkins
Oliver Watkins

Reputation: 13539

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?

Upvotes: 3

Views: 1636

Answers (2)

x00
x00

Reputation: 13843

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.

Upvotes: 1

HenriDev
HenriDev

Reputation: 697

i think you should mock your return from the useAsync hook itself.

jest.mock("path/to/useAsync", () => ({
  result: ["test"]
}));`

Upvotes: 0

Related Questions