用 Jest 模拟 React 自定义钩子

Posted

技术标签:

【中文标题】用 Jest 模拟 React 自定义钩子【英文标题】:Mocking React custom hook with Jest 【发布时间】:2021-03-23 23:38:28 【问题描述】:

在对 React 组件进行单元测试时,我需要模拟我的自定义钩子。我已经阅读了一些关于这个简单任务的教程和 *** 答案,但没有正确实现它。

我最简单的单一测试设置如下:

// TestComponent.js

import React from "react";
import useTest from "./useTest";

const TestComponent = () => 
  const  state  = useTest("initial_value");

  return <div>state</div>;
;

export default TestComponent;
// useTest.jsx - simple custom hook

import React,  useState  from "react";

const useTest = (initialState) => 
  const [state] = useState(initialState);
  return  state ;
;

export default useTest;

// TestComponent.test.jsx - my test case

import React from "react";
import  render  from "@testing-library/react";
import TestComponent from "./TestComponent";

jest.mock("./useTest", () => (
  useTest: () => "mocked_value",
));

test("rendertest", () => 
  const component = render(<TestComponent />);
  expect(component.container).toHaveTextContent("mocked_value");
);

所以我试图模拟 useTest 自定义钩子以返回“mocked_value”,而不是来自真正的自定义钩子的“initial_value”。但上面的代码只是给了我这个错误:

TypeError: (0 , _useTest.default) is not a function

      3 | 
      4 | const TestComponent = () => 
    > 5 |   const  state  = useTest("initial_value");
        |                     ^
      6 | 
      7 |   return <div>state</div>;
      8 | ;

我也试过了:

import useTest from './useTest';
// ...
jest.spyOn(useTest, "useTest").mockImplementation(() => "mocked_value");
import useTest from './useTest';
// ...
jest.spyOn(useTest, "useTest").mockReturnValue("mocked_value");

但两者都给我错误Cannot spy the useTest property because it is not a function; undefined given instead

如何实施这个测试?

【问题讨论】:

【参考方案1】:

我在回答自己。这样就可以了:

jest.mock("./useTest", () => (
  useTest: () => ( state: 'mocked_value' ),
));

如果我想在自定义挂钩中使用默认导出:

jest.mock("./useTest", () => (
  __esModule: true,
  default: () => ( state: 'mocked_value' ),
));

另外,如果我还想在我的钩子中使用setState 方法并导出它,我可以这样模拟它:

const mockedSetState = jest.fn();

jest.mock("./useTest", () => (
  useTest: () => ( state, setState: mockedSetState ),
));

现在可以检查setState 是否被调用过一次:

expect(mockedSetState).toHaveBeenCalledTimes(1);

【讨论】:

非常感谢。 默认导出解决方案非常适合我!谢谢!【参考方案2】:

由于您在 useTest 模块中使用export default useTest,它希望从您的模拟中的默认属性中获取该函数引用。

试试这个:

jest.mock("./useTest", () => (
  default: () => "mocked_value",
));

如果你想避免混淆,你可以在 useTest 模块中尝试export const useTest = ...,然后在你的组件中尝试import useTest from './useTest'。如果使用这种方法,则无需更改测试。

【讨论】:

感谢您的提示,但这也不起作用。我尝试在jest.mock 函数中将“useTest”更改为“默认”,还尝试使用export default useTest... 导出useTest 挂钩,但没有一个有效。总是同样的错误TypeError: (0 , _useTest.default) is not a function 我通过稍微修改它使其工作(见我自己的答案),但感谢这个导出技巧,这很有价值!

以上是关于用 Jest 模拟 React 自定义钩子的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 JEST、Enzyme 在 React 中测试自定义钩子?

如何使用 react-hooks-testing-library 测试自定义 async/await 钩子

如何为每个测试模拟不同的 useLocation 值?

使用 SetTimeout 测试自定义钩子,使用 Jest 测试 useEffect

用 Jest 模拟库钩子

React Jest 测试因 MSW 失败