135

Here is my custom hook:

  export function useClientRect() {
    const [scrollH, setScrollH] = useState(0);
    const [clientH, setClientH] = useState(0);
    const ref = useCallback(node => {
      if (node !== null) {
        setScrollH(node.scrollHeight);
        setClientH(node.clientHeight);
      }
    }, []);
    return [scrollH, clientH, ref];
  }
}

I want each time that it is called, it return my values. like:

jest.mock('useClientRect', () => [300, 200, () => {}]);

How can I achieve this?

4

4 Answers 4

249

Load the hook as a module. Then mock the module:

jest.mock('module_path/module_name', () => ({
    useClientRect: () => [300, 200, jest.fn()]
}));

mock should be called on top of the file outside test fn. Therefore we are going to have only one array as the mocked value.

If you want to mock the hook with different values in different tests:

import * as hooks from 'module_path/module_name';

it('a test', () => {
    jest.spyOn(hooks, 'useClientRect').mockImplementation(() => ([100, 200, jest.fn()]));
    //rest of the test
});
Sign up to request clarification or add additional context in comments.

11 Comments

mother of god, pointing out that it needed to be at the top was helpful - somehow no one has mentioned this in the dozen other tutorials i've looked at
@MuhammadHaseeb You need to replace useClientRect with the name of your hook. In this example the hook returns an array that needs to be mocked.
@muhammad-haseeb you need to replace the array with a mocked object. In your code console.log the value that your hook is returning and replace the array like: jest.spyOn(hooks, 'useYourHookName').mockImplementation(() => ({a: 100, b: 200, c: jest.fn()}));.
@Mohammad Kermani when you have a hook file, it cab be something like '../folder_name/hook_file_name.ts'. when you mock a hook from a npm library it will be the name of the npm library.
In my particular case, I had to do import hooks from 'module_name instead of import * as hooks from 'module_name'. With the latter, the top-level mock was called, but the spy was not.
|
74

Adding on to this answer for typescript users encountering the TS2339: Property 'mockReturnValue' does not exist on type error message. There is now a jest.mocked helper you can call to mock with Type defs (which is a port of the ts-jest/utils mocked function).

import useClientRect from './path/to/useClientRect';

jest.mock('./path/to/useClientRect');

const mockUseClientRect = jest.mocked(useClientRect);

describe("useClientRect", () => {
  it("mocks the hook's return value", () => {
    mockUseClientRect.mockReturnValue([300, 200, () => {}]);
    // ... do stuff
  });

  it("mocks the hook's implementation", () => {
    mockUseClientRect.mockImplementation(() => [300, 200, () => {}]);
    // ... do stuff
  });
});

1 Comment

especially for Typescript usage this provides the best answer! thanks
19

Well, this is quite tricky and sometimes developers get confused by the library but once you get used to it, it becomes a piece of cake. I faced a similar issue a few hours back and I'm sharing my solution for you to derive your solution easily.

My custom Hook:

  import { useEffect, useState } from "react";
  import { getFileData } from "../../API/gistsAPIs";
    
  export const useFilesData = (fileUrl: string) => {
    const [fileData, setFileData] = useState<string>("");
    const [loading, setLoading] = useState<boolean>(false);
    useEffect(() => {
      setLoading(true);
      getFileData(fileUrl).then((fileContent) => {
        setFileData(fileContent);
        setLoading(false);
      });
    }, [fileUrl]);
    
    return { fileData, loading };
  };

My mock code: Please include this mock in the test file outside of your test function. Note: Be careful about the return object of mock, it should match with the expected response.

const mockResponse = {
  fileData: "This is a mocked file",
  loading: false,
};
jest.mock("../fileView", () => {
  return {
    useFilesData: () => {
      return {
        fileData: "This is a mocked file",
        loading: false,
      };
    },
  };
});

The complete test file would be:

import { render, screen, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import FileViewer from "../FileViewer";

const mockResponse = {
  fileData: "This is a mocked file",
  loading: false,
};
jest.mock("../fileView", () => {
  return {
    useFilesData: () => {
      return {
        fileData: "This is a mocked file",
        loading: false,
      };
    },
  };
});

describe("File Viewer", () => {
  it("display the file heading", async () => {
    render(<FileViewer fileUrl="" filename="regex-tutorial.md" className="" />);
    const paragraphEl = await screen.findByRole("fileHeadingDiplay");
    expect(paragraphEl).toHaveTextContent("regex-tutorial.md");
  });
}

2 Comments

How would you share the mock between tests? Any idea?
What is the point of mockResponse variable? You don't use it anywhere?
1

In my case I resolved it doing this:

jest.mock('pathToTheHookFile/useClientRect');

describe('It tests the hook', ()=> {
  beforeEach(() => {
    useClientRect.mockReturnValue([300, 200, jest.fn()])
  });

  it('Tests something', ()=> {
    ...
  }
});

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.