You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Query乐观更新测试问题求助:用例失效及mock方法咨询

React Query 乐观更新测试失败排查及方法Mock咨询

我正在为React Query中的乐观更新编写测试用例,但测试无法正常运行。仅保留onSuccess逻辑时功能正常,添加乐观更新相关逻辑后就失效了。附上我的测试代码和自定义Hook代码,希望能得到帮助,同时想咨询如何在测试中mock getQueryData和setQueryData方法。


测试代码

import { act, renderHook } from "@testing-library/react-hooks";
import axios from "axios";
import { createWrapper } from "../../test-utils";
import { useAddColorHook, useFetchColorHook } from "./usePaginationReactQuery";
jest.mock("axios");

describe('Testing custom hooks of react query', () => {

    it('Should add a new color', async () => {
        axios.post.mockReturnValue({data: [{label: 'Grey', id: 23}]})
        const { result, waitFor } = renderHook(() => useAddColorHook(1), { wrapper: createWrapper() });
        await act(() => {
            result.current.mutate({ label: 'Grey' })
        })
        await waitFor(() => result.current.isSuccess);
    })
})

export const createTestQueryClient = () =>
  new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
        cacheTime: Infinity,
      },
    },
    logger: {
      log: console.log,
      warn: console.warn,
      error: () => {},
    }
  });

export function createWrapper() {
  const testQueryClient = createTestQueryClient();
  return ({ children }) => (
    <QueryClientProvider client={testQueryClient}>
      {children}
    </QueryClientProvider>
  );
}

自定义Hook代码

export const useAddColorHook = (page) => {
    const queryClient = useQueryClient()
    return useMutation(addColor, {
        // onSuccess: () => {
        //     queryClient.invalidateQueries(['colors', page])
        // }
        onMutate: async color => {
            // newHero refers to the argument being passed to the mutate function
            await queryClient.cancelQueries(['colors', page])
            const previousHeroData = queryClient.getQueryData(['colors', page])
            queryClient.setQueryData(['colors', page], (oldQueryData) => {
                return {
                    ...oldQueryData,
                    data: [...oldQueryData.data, { id: oldQueryData?.data?.length + 1, ...color }]
                }
            })
            return { previousHeroData }
        },
        onSuccess: (response, variables, context) => {
            queryClient.setQueryData(['colors', page], (oldQueryData) => {
                console.log(oldQueryData, 'oldQueryData', response, 'response', variables, 'var', context, 'context', 7984)
                return {
                    ...oldQueryData,
                    data: oldQueryData.data.map(data => data.label === variables.label ? response.data : data)
                }
            })
        },
        onError: (_err, _newTodo, context) => {
            queryClient.setQueryData(['colors', page], context.previousHeroData)
        },
        onSettled: () => {
            queryClient.invalidateQueries(['colors', page])
        }
    })
}

问题排查与解决

1. 测试失败的核心原因

测试中未预先为['colors', 1]查询键设置初始缓存数据,导致onMutate阶段调用getQueryData返回undefined,后续执行oldQueryData.data时触发报错,直接导致测试流程中断。仅保留onSuccess时,逻辑不依赖缓存初始数据,因此不会触发该错误。

2. 修复测试的具体步骤

步骤1:修改测试代码,预先设置缓存初始数据

it('Should add a new color', async () => {
    // 注意:axios.post需用mockResolvedValue,因为原方法返回Promise
    axios.post.mockResolvedValue({data: [{label: 'Grey', id: 23}]})
    
    // 创建测试用QueryClient并设置初始缓存
    const testQueryClient = createTestQueryClient();
    testQueryClient.setQueryData(['colors', 1], {
        data: [{label: 'Red', id: 1}] // 模拟已有数据
    });

    // 将预设置好的QueryClient传入wrapper
    const { result, waitFor } = renderHook(() => useAddColorHook(1), { 
        wrapper: createWrapper(testQueryClient)
    });

    await act(async () => {
        result.current.mutate({ label: 'Grey' })
    })
    await waitFor(() => result.current.isSuccess);

    // 验证缓存数据是否正确更新
    const updatedData = testQueryClient.getQueryData(['colors', 1]);
    expect(updatedData.data).toContainEqual({label: 'Grey', id: 23});
})

步骤2:修改createWrapper,支持传入自定义QueryClient

export function createWrapper(testQueryClient) {
  // 优先使用传入的client,否则创建新实例
  const client = testQueryClient ?? createTestQueryClient();
  return ({ children }) => (
    <QueryClientProvider client={client}>
      {children}
    </QueryClientProvider>
  );
}

3. Mock getQueryData和setQueryData的方法

若需单独mock这两个方法,可通过jest spyOn监听测试用QueryClient实例:

const testQueryClient = createTestQueryClient();

// Mock getQueryData
const mockGetQueryData = jest.spyOn(testQueryClient, 'getQueryData')
  .mockReturnValue({data: [{label: 'Blue', id: 2}]});

// Mock setQueryData
const mockSetQueryData = jest.spyOn(testQueryClient, 'setQueryData')
  .mockReturnValue();

// 测试后清理mock
afterEach(() => {
  jest.clearAllMocks();
});

内容的提问来源于stack exchange,提问作者Rohit Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 13:45:38