我有一个自定义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 和测试:
-
Mock
queryApi
:
使用jest.mock('./api')
来模拟整个./api
模块,并设置queryApi
的 mock 行为。 -
编写测试:
使用@testing-library/react-hooks
的renderHook
函数来渲染 Hook,并使用act
和waitFor
(或waitForNextUpdate
,如果它适用于你的场景)来处理异步操作。 -
验证状态和行为:
检查 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,那么你可能需要使用不同的方法来等待组件的渲染和状态更新。