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

如何测试基于useQuery结果的条件渲染React组件?

测试基于TanStack Query的条件渲染组件

问题背景

组件通过useQuery异步获取分类数据,仅当categories存在时渲染Select组件。测试时无法找到combobox或option元素(报错:TestingLibraryElementError: Unable to find an accessible element with the role "option"),但未使用TanStack Query前测试逻辑正常。

组件核心代码

const { data: categories, isLoading } = useQuery<ICategoryItem[], boolean>({
  queryKey: ['categoryItem'],
  queryFn: () => getCategoryItem(userId),
  enabled: userId !== '',
})

return (
  {categories && (
    <div>
      <Select
        value={category.label}
        onValueChange={(value) => {
          const category = categories.filter(
            (category: ICategoryItem) => category._id === value
          )[0]
          setCategory(() => category)
        }}
      >
        {/* 选项渲染逻辑 */}
      </Select>
    </div>
  )}
)

原测试代码(无效)

it('test', async () => {
  const user = userEvent.setup()
  const screen = render(<AtvtSection currentDate={new Date()} />)

  await user.click(screen.getByRole('combobox'))
  const comboboxList = screen.getAllByRole('option')
  await user.click(comboboxList[0])

  // ...
})

尝试过的无效模拟方式

function useCustomHook() {
  return useQuery({
    queryKey: ['categoryItem'],
    queryFn: () => {
      return [
        {
          _id: '66001e98d44dc9956110d48a',
          label: '💻',
          value: 'studying',
          userId: 'public',
        },
        {
          _id: '6602463b6e2d7d198cba1c0f',
          label: '💪',
          value: 'exercise',
          userId: 'public',
        },
      ]
    },
  })
}

const { result: categories } = renderHook(() => useCustomHook(), { wrapper })
console.log(categories)
await waitFor(() => categories.current.isSuccess)
expect(categories.current.data).toBeDefined()

解决方案

方法1:使用QueryClient预填充缓存(推荐)

利用TanStack Query的缓存机制,提前注入模拟数据,让组件直接读取缓存而非发起真实请求:

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { userEvent, render, waitFor } from '@testing-library/react';

it('测试分类选择器交互', async () => {
  const user = userEvent.setup();
  
  // 创建QueryClient实例,关闭重试避免测试等待
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
    },
  });

  // 预存`categoryItem`查询的模拟数据
  queryClient.setQueryData(['categoryItem'], [
    {
      _id: '66001e98d44dc9956110d48a',
      label: '💻',
      value: 'studying',
      userId: 'public',
    },
    {
      _id: '6602463b6e2d7d198cba1c0f',
      label: '💪',
      value: 'exercise',
      userId: 'public',
    },
  ]);

  // 用QueryClientProvider包裹组件,注入缓存数据
  render(
    <QueryClientProvider client={queryClient}>
      <AtvtSection currentDate={new Date()} />
    </QueryClientProvider>
  );

  // 等待combobox渲染完成
  await waitFor(() => {
    expect(screen.getByRole('combobox')).toBeInTheDocument();
  });

  await user.click(screen.getByRole('combobox'));
  // 用findAllByRole等待option加载(异步渲染)
  const options = await screen.findAllByRole('option');
  await user.click(options[0]);

  // 后续断言逻辑,例如检查选中值是否正确
});

方法2:直接Mock useQuery钩子

用Jest直接mockuseQuery的返回值,强制组件使用模拟数据:

import { useQuery } from '@tanstack/react-query';
import { userEvent, render, waitFor } from '@testing-library/react';

// Mock整个useQuery模块
jest.mock('@tanstack/react-query');

it('测试分类选择器交互', async () => {
  const user = userEvent.setup();
  
  // 设定useQuery的mock返回值
  (useQuery as jest.Mock).mockReturnValue({
    data: [
      {
        _id: '66001e98d44dc9956110d48a',
        label: '💻',
        value: 'studying',
        userId: 'public',
      },
      {
        _id: '6602463b6e2d7d198cba1c0f',
        label: '💪',
        value: 'exercise',
        userId: 'public',
      },
    ],
    isLoading: false,
    isSuccess: true,
  });

  render(<AtvtSection currentDate={new Date()} />);

  await waitFor(() => {
    expect(screen.getByRole('combobox')).toBeInTheDocument();
  });

  await user.click(screen.getByRole('combobox'));
  const options = await screen.findAllByRole('option');
  await user.click(options[0]);

  // 后续断言逻辑
});

关键注意点

  1. 异步数据处理:useQuery是异步操作,测试时必须用waitFor或findBy*系列查询方法,不能直接用getBy*(会因元素未渲染报错)
  2. userId依赖:组件中useQuery的enabled参数依赖userId,需确保测试时组件能获取到有效userId(如通过props或上下文传入)
  3. 无效方法原因:之前自定义useCustomHook并通过renderHook测试,仅验证了钩子本身,并未将模拟数据注入到AtvtSection组件中,组件仍会发起真实请求导致无数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 09:44:56