【问题标题】:How to test a component which is using a custom TypeScript React Hook?如何测试使用自定义 TypeScript React Hook 的组件?
【发布时间】:2020-01-15 11:58:42
【问题描述】:

我目前正在 Typescript 中编写一个 React 组件,它使用一个名为 useAxios 的 axios-hooks 钩子。这个钩子的一个使用例子是here:

 export const App = () => {
  const [{ data, loading, error }, refetch] = useAxios(
    "https://api.myjson.com/bins/820fc"
  );

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error!</p>;

  return (
    <div>
      <button onClick={e => refetch()}>refetch</button>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

const rootElement = document.getElementById("root");
render(<App />, rootElement);

我试图弄清楚如何编写一个可以模拟 useAxios 钩子的测试。我尝试创建底层 axios 组件的模拟,但无法正常工作:

import React from "react"
import { render } from "@testing-library/react"
import { Test } from "../test"
import useAxios from "axios-hooks"

jest.mock("axios-hooks")
const mockedAxios = useAxios as jest.Mocked<typeof useAxios>

it("Displays loading", () => {
  // How to mock the return values in the following line?
  // mockedAxios.

  const { getByText } = render(<Test />)

  expect(getByText("Loading...")).toBeDefined()
})

我知道我不应该模拟作为底层依赖项的 axios,我应该能够模拟 useAxios,但无论如何我都会尝试。

我意识到这个问题在 SO 上已经被多次提及,但我可以找到解决这个特定用例的方法。

非常感谢任何帮助!

【问题讨论】:

  • 如果你jest.mock("axios-hooks") 和后来的useAxious.mockResolvedValue(dataToRespond) 会发生什么?还是您需要基于 URL 的动态模拟?
  • 当我使用打字稿时,我似乎无法做到这一点。我认为有办法使用 TS 做到这一点?
  • 所以问题特定于 TS 中的类型检查,对吧?对我来说,它看起来像是测试运行,但模拟并没有在这样的事情上工作
  • @skyboyer 我用示例测试更新了我的问题。我试图弄清楚如何模拟 TS 中的钩子返回值。

标签: reactjs typescript jestjs axios react-hooks


【解决方案1】:

模拟模块并为每个测试设置useAxios 的预期结果,例如

jest.mock('axios-hooks');

import useAxios from 'axios-hooks';

test('App displays loading when request is fetching', () => {
  useAxios.mockReturnValue(Promise.resolve({ loading: true }));
  // mount component
  // Verify "Loading" message is rendered
});

【讨论】:

  • 我正在使用 TypeScript,但这个答案似乎不起作用
  • @BenSmith TS 不应该真正参与其中,究竟是什么不起作用?
  • 看来确实如此。另请参阅我更新的问题,其中包含用 TS 编写的不完整测试。
  • @BenSmith TS 应该是非侵入式的(除非改变了),所以上面的代码应该运行。看看你的例子,你已经添加了const mockedAxios = useAxios as jest.Mocked&lt;typeof useAxios&gt;,它把我们带到了 TS 领域,但这真的有必要吗?您是否按原样尝试过没有任何 TS 的代码?
【解决方案2】:

我自己想出了如何做到这一点。为了测试自定义钩子,我做了以下操作:

import * as useAxios from "axios-hooks"
jest.mock("axios-hooks")
const mockedAxios = useAxios as jest.Mocked<typeof useAxios>

it("Displays loading message", async () => {

  // Explicitly define what should be returned
  mockedAxios.default.mockImplementation(() => [
      {
        data: [],
        loading: true,
        error: undefined
      },
      () => undefined
    ])

  const { getByText } = render(<Test />)

  expect(getByText("Loading...")).toBeDefined()
})

【讨论】:

    【解决方案3】:

    为了让编译器对() =&gt; undefined 参数感到满意,我不得不跳过一些额外的环节。我不是双重as 的粉丝,但我不知道如何使它不那么冗长,因为我是 TS 新手。

    import * as useAxios from 'axios-hooks';
    import { AxiosPromise } from 'axios';
    import React from 'react';
    import Test from 'components/Test';
    import { render } from '@testing-library/react';
    
    jest.mock('axios-hooks');
    const mockedUseAxios = useAxios as jest.Mocked<typeof useAxios>;
    
    it('renders a loading message', async () => {
      mockedUseAxios.default.mockImplementation(() => [
        {
          data: [],
          loading: true,
          error: undefined,
        },
        (): AxiosPromise => (undefined as unknown) as AxiosPromise<unknown>,
      ]);
    
      const { getByText } = render(<Test />);
    
      expect(getByText('Loading...')).toBeDefined();
    });
    

    【讨论】:

    • 您的答案与我的完全相同,因为返回函数的变化。你为什么不发表评论?
    • 我考虑了这一点,但我的回答还包括额外的导入语句以及您的示例中缺少的相关导入。非常感谢您在这里提出问题并为故障排除提供良好的起点。有时,在单个标记实例中以整体合理的方法获得答案会很好。
    • 好吧,说得很好,我很高兴能帮上忙。我经常参考我自己的答案来嘲笑这个库,因为它每次都让我着迷!
    猜你喜欢
    • 1970-01-01
    • 2022-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-23
    • 2021-12-13
    • 1970-01-01
    • 2021-06-18
    相关资源
    最近更新 更多