jest测试带异步操作的自定义hook

我有一个自定义hook:useA的返回值{reportData},这reportData来自于在useA的useEffect中调用了接口queryApi,有异步操作,我想写这个组件的单元测试,我想mock这个queryApi的返回值,判断这个返回值和reanderHook(()=>useA)获得的值是否一样,请问该如何mock?

这段很重要,异步函数要加await act;

      await waitForNextUpdate();
    });

有异步函数的需要用到waitForNextUpdate,如const { result, waitForNextUpdate } = renderHook(() => useA());

另外renderHook从这里导入@testing-library/react-hooks,如import { renderHook, act } from ‘@testing-library/react-hooks/dom’;

如果 useA 自定义 Hook 中包含异步操作,并且这些异步操作依赖于从 ./api 模块导入的 queryApi 函数,那么在测试中模拟这些异步操作的关键是确保 queryApi 被正确地 mock,并且你能够验证 Hook 的行为(包括其状态更新和副作用)是否按预期进行。

以下是一些步骤和示例,展示如何在这种情况下进行 mock 和测试:

  1. Mock queryApi
    使用 jest.mock('./api') 来模拟整个 ./api 模块,并设置 queryApi 的 mock 行为。

  2. 编写测试
    使用 @testing-library/react-hooksrenderHook 函数来渲染 Hook,并使用 actwaitFor(或 waitForNextUpdate,如果它适用于你的场景)来处理异步操作。

  3. 验证状态和行为
    检查 Hook 的返回值和状态是否按预期更新。

下面是一个具体的示例:

// useA.js
import { useState, useEffect } from 'react';
import { queryApi } from './api';

function useA() {
  const [reportData, setReportData] = useState(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      try {
        const data = await queryApi();
        setReportData(data);
      } catch (err) {
        setError(err);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();
  }, []); // 空依赖数组表示只在组件挂载时运行

  return { reportData, isLoading, error };
}

// useA.test.js
import { renderHook, act } from '@testing-library/react-hooks/dom';
import useA from './useA';
import * as api from './api';

jest.mock('./api');

describe('useA', () => {
  it('should fetch data successfully', async () => {
    // Mock the queryApi to resolve with some data
    api.queryApi.mockResolvedValue({ someField: 'mocked data' });

    const { result, waitForNextUpdate } = renderHook(() => useA());

    // Wait for the async operation to complete
    await act(async () => {
      await waitForNextUpdate();
    });

    // Verify the results
    expect(result.current.reportData).toEqual({ someField: 'mocked data' });
    expect(result.current.isLoading).toBe(false);
    expect(result.current.error).toBe(null);
  });

  it('should handle errors gracefully', async () => {
    // Mock the queryApi to reject with an error
    api.queryApi.mockRejectedValue(new Error('API failed'));

    const { result, waitForNextUpdate } = renderHook(() => useA());

    // Wait for the async operation to complete
    await act(async () => {
      await waitForNextUpdate();
    });

    // Verify the error state
    expect(result.current.reportData).toBe(null);
    expect(result.current.isLoading).toBe(false);
    expect(result.current.error).toBeInstanceOf(Error);
    expect(result.current.error.message).toBe('API failed');
  });
});

在这个示例中,我们使用了 jest.mock('./api') 来模拟 ./api 模块,并设置了 queryApi 的 mock 行为。然后,我们编写了两个测试用例:一个用于验证成功的数据获取,另一个用于验证错误处理。

注意,由于 useA Hook 中的异步操作是在 useEffect 钩子中触发的,并且 useEffect 只在组件挂载时运行一次(由于依赖数组为空),因此我们只需要在测试中等待一次更新即可。我们使用 waitForNextUpdate(尽管在这个特定的例子中,由于 useEffect 只运行一次,waitFor 可能更合适,但 waitForNextUpdate 也能工作)来等待异步操作完成,并验证 Hook 的状态是否按预期更新。

另外,请注意,waitForNextUpdate 并不是 @testing-library/react-hooks 的一部分,但你可以使用 waitFor(来自 @testing-library/dom 或其他类似的库)或自定义逻辑来等待异步操作完成。然而,在这个例子中,由于我们直接测试的是 Hook 而不是组件,所以 waitForNextUpdate(或类似的机制)是检查 Hook 状态更新的有效方法。如果你正在测试一个组件,并且该组件使用了 useA Hook,那么你可能需要使用不同的方法来等待组件的渲染和状态更新。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值