用 Jest 模拟库钩子

Posted

技术标签:

【中文标题】用 Jest 模拟库钩子【英文标题】:Mocking library hook with Jest 【发布时间】:2021-05-11 07:06:33 【问题描述】:

设置

我在我的一个组件中使用了一个名为 react-firebase-hooks 的库。

// Component.tsx
import React, useEffect from 'react';
import  Firebase  from '../firebase/firebase';
import  useAuthState  from 'react-firebase-hooks/auth';


export const Component = () => 
  // Note that Firebase.getAuth is a static method of a class of mine for getting the firebase authentication object
  const [user, loading ] = useAuthState(Firebase.getAuth());


  if (!loading && !user) 
    // ... stuff
  

  return (
    <div>

      ...stuff
    </div>
  );
;

一种有效的方式

我试图在我的一项测试中模拟出useAuthState 调用,但在找出执行模拟的最佳方法时遇到了很多麻烦。我找到了一个可行的解决方案:

// Component.test.tsx
import useAuthState from 'react-firebase-hooks/auth'

jest.mock('react-firebase-hooks/auth', () => (
  useAuthState: jest.fn()
));

但是,上面的模拟实现使得模拟对象无法在 IDE 中完成。例如,这是我尝试设置一个模拟返回值:

      test(`component correctly loads`, () => 
         useAuthState.mockReturnValue([true, false])

        // renderApp is a utility method of mine to render using react-testing-library
        const  getByText  = renderApp();

        // expect some stuff
      );

我的首选结果(和尝试 1)

我发现useAuthState 没有自动完成功能。

理想情况下,我希望声明模拟的方式更接近

const mockAuth = jest.fn();
jest.mock('react-firebase-hooks/auth', () => 
  return jest.fn().mockImplementation(() => 
    return useAuthState: mockAuth
  )
);

然后让我通过我声明的测试(并获得自动完成)很好地改变 mockAuth 对象,例如:

      test(`component correctly loads`, () => 
         mockAuth.mockReturnValue([true, false])

        // renderApp is a utility method of mine to render using react-testing-library which renders Component
        const  getByText  = renderApp();

        // expect some stuff
      );

但是,通过上述实现,我收到以下错误:(0 , _auth.useAuthState) is not a function or its return value is not iterable in Component

尝试 2

我也尝试过将模拟声明为

const mockAuth = jest.fn();
jest.mock('react-firebase-hooks/auth', () => (
    useAuthState: mockAuth
));

但这给了我ReferenceError: Cannot access 'mockAuth' before initialization的错误。

尝试 3

我也试过

const mockAuth = jest.fn();
import useAuthState from 'react-firebase-hooks/auth'
jest.mock('react-firebase-hooks/auth', () => (
    useAuthState: mockAuth
));

但这给出了同样的错误。

结束问题

有没有办法让我在模拟声明的“外部”声明一个模拟函数,以便我可以自动完成它?

【问题讨论】:

看这个***.com/questions/57305823/…或者创建沙盒 我看不出这如何有助于在模块模拟声明之外声明模拟函数。 你能做codesanbox吗? @DaniilLoban CodeSandbox doesn't support jest mocking。我必须以我认为你想知道的艰难方式弄清楚这一点。 我只需要最小的安装项目,而不是在 sunbox 中运行 【参考方案1】:

如果您使用另一个文件进行模拟,您应该很容易更改实现并仍然保留 TypeScript Intellisense。

src/App.test.jsx

import App from "../App";
import  render  from "@testing-library/react";
import  useAuthState  from "../__mocks__/react-firebase-hooks/auth";

it("should be mocked", () => 
  // Change the implementation of the mock
  useAuthState.mockReturnValue([true, false]);
  const  getByText  = render(<App />);
  // Expect that the app file got the mocked value
  expect(useAuthState).toBeCalledWith("my auth");
  expect(getByText("true,false")).toBeTruthy();
);

src/__mocks__/react-firebase-hooks/auth.js

export const useAuthState = jest.fn();

src/App.js

import  useAuthState  from "react-firebase-hooks/auth";

export default function App() 
  const test = useAuthState("my auth");
  return <div>test.toString()</div>;

【讨论】:

哇,太酷了。我以前没有见过这种语法。你有没有我可以阅读的文档的链接?这是模拟的首选方式吗?这种方法有什么缺点? 我能想到的唯一缺点是必须制作一个单独的文件。有时这是理想的,有时它有点烦人。这是相关的文档(尽管在这个特定模型上很少)jestjs.io/docs/en/manual-mocks

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

使用 jest 模拟 react-router-dom 钩子不起作用

模拟安装挂钩 Jest 测试单元

如何使用 jest.mock 模拟 useRef 和反应测试库

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

用 Jest 模拟 React-Redux 商店

使用 Jest 在 Nuxt 中测试组件时如何添加/模拟 Nuxt Auth 库