如何模拟反应自定义钩子返回值?

Hom*_*oma 28 reactjs jestjs react-testing-library react-hooks

这是我的自定义钩子:

  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];
  }
}
Run Code Online (Sandbox Code Playgroud)

我希望每次调用它时,它都会返回我的值。喜欢:

jest.mock('useClientRect', () => [300, 200, () => {}]);
Run Code Online (Sandbox Code Playgroud)

我怎样才能做到这一点?

Hom*_*oma 65

将钩子作为模块加载。然后模拟模块:

jest.mock('module_name', () => ({
    useClientRect: () => [300, 200, jest.fn()]
}));
Run Code Online (Sandbox Code Playgroud)

mock 应该在 test fn 之外的文件顶部调用。因此,我们将只有一个数组作为模拟值。

如果你想在不同的测试中用不同的值模拟钩子:

import * as hooks from 'module_name';

it('a test', () => {
    jest.spyOn(hooks, 'useClientRect').mockImplementation(() => ([100, 200, jest.fn()]));
    //rest of the test
});

Run Code Online (Sandbox Code Playgroud)

  • 天哪,指出它需要位于顶部是有帮助的 - 不知怎的,在我看过的其他十几个教程中没有人提到这一点 (19认同)
  • @MuhammadHaseeb 您需要将 useClientRect 替换为您的钩子名称。在此示例中,挂钩返回一个需要模拟的数组。 (2认同)
  • @muhammad-haseeb,您需要用模拟对象替换数组。在代码 console.log 中,记录钩子返回的值并替换数组,例如:`jest.spyOn(hooks, 'useYourHookName').mockImplementation(() => ({a: 100, b: 200, c: jest .fn()}));`。 (2认同)
  • @Mohammad Kermani,当你有一个钩子文件时,它可能类似于“../folder_name/hook_file_name.ts”。当您模拟 npm 库中的钩子时,它将是 npm 库的名称。 (2认同)

小智 58

为遇到错误消息的打字稿用户添加此答案TS2339: Property 'mockReturnValue' does not exist on type。现在有一个jest.mocked帮助器,您可以使用 Type defs 来调用模拟(这是 ts-jest/utilsmocked函数的端口)。

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
  });
});
Run Code Online (Sandbox Code Playgroud)


小智 17

嗯,这是相当棘手的,有时开发人员会对这个库感到困惑,但一旦你习惯了它,它就变得小菜一碟了。几个小时前我遇到了类似的问题,我正在分享我的解决方案,以便您轻松得出解决方案。

我的自定义钩子:

  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 };
  };
Run Code Online (Sandbox Code Playgroud)

我的模拟代码:请将此模拟包含在测试函数之外的测试文件中。注意:请注意mock的返回对象,它应该与预期的响应相匹配。

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

完整的测试文件将是:

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");
  });
}
Run Code Online (Sandbox Code Playgroud)

  • mockResponse 变量有什么意义?你不在任何地方使用它吗? (8认同)