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

如何用React Testing Library测试基于fixedCacheQuery的RTK Query组件B?

测试使用RTK Query懒加载的组件B的解决方案

组件A

负责执行用户懒加载,代码如下:

const ComponentA = () => {
  const [trigger, {data}] = useLazyLoadUsers({
    fixedCacheKey: fixedLoadUsersKey,
  });

  useEffect(() => {
    trigger();
  }, []);

  return <div>{data.map(user => user.id)}</div>
}

组件B

需要在useLazyLoadUsers的isLoading为true时显示加载指示器,代码如下:

const ComponentB = () => {
  const [, {isLoading}] = useLazyLoadUsers({
    fixedCacheKey: fixedLoadUsersKey,
  });

  if (!isLoading) {
    return <div>Users loaded</div>
  }

  return <div>Loading users</div>
}

问题描述

通过fixedCacheKey已实现两个组件的状态同步且运行正常,但找不到测试Component B的相关文档或示例。测试Component A的方法在Redux官方文档有说明。

我已有一个自定义的React Testing Library render方法,可提供包含所有自动生成查询的真实Store。希望通过模拟isLoading值来测试Component B是否渲染加载指示器,不想将isLoading状态复制到另一个slice中。

尝试过mock useLazyLoadUsers但未成功,也试过在渲染测试前dispatch初始化操作:

it('should render the loading indicator', async () => {
  const store = makeMockedStore();
  store.dispatch(myApi.endpoints.loadUsers.initiate());
  render(<ComponentB />, {store});
  expect(await screen.findByText('Loading users')).toBeVisible();
})

但该方法无效,求解决思路或最佳实践。


解决思路与最佳实践

方案1:手动设置RTK Query缓存状态

既然使用真实Store,无需mock整个hook,可直接在测试前手动更新RTK Query的缓存状态,将对应查询的isLoading设为true。

RTK Query的状态存在state.api.queries下,针对你的fixedCacheKey,可直接修改Store初始状态:

it('should render the loading indicator', () => {
  const store = makeMockedStore({
    api: {
      queries: {
        [fixedLoadUsersKey]: {
          isLoading: true,
          status: 'pending' // 补充必要的默认状态字段
        }
      }
    }
  });

  render(<ComponentB />, {store});
  expect(screen.getByText('Loading users')).toBeVisible();
})

如果makeMockedStore不支持传入初始状态覆盖,可在dispatch后手动patch Store状态,或结合api.util.updateQueryData工具调整,但初始化时直接设置状态更高效。

方案2:正确处理异步状态更新

你之前的代码问题在于,dispatchinitiate()后,RTK Query的状态更新是异步的,直接渲染组件时状态可能尚未完成更新,导致断言失败。需要用waitFor等待组件状态同步:

it('should render the loading indicator', async () => {
  const store = makeMockedStore();
  // 发起请求并保存promise,用于后续控制状态
  const requestPromise = store.dispatch(myApi.endpoints.loadUsers.initiate()).unwrap();
  
  render(<ComponentB />, {store});
  
  // 等待组件感知到loading状态并更新渲染
  await waitFor(() => {
    expect(screen.getByText('Loading users')).toBeVisible();
  });

  // 若需测试加载完成状态,可等待请求promise resolve
  await requestPromise;
  expect(screen.getByText('Users loaded')).toBeVisible();
})

注意要确保makeMockedStore正确配置了RTK Query中间件,且mock的API请求处于pending状态(比如用jest.useFakeTimers()延迟响应),否则请求会立即完成,isLoading仅瞬间为true,导致断言失败。

方案3:正确Mock useLazyLoadUsers Hook

之前mock失败大概率是路径或返回值结构错误。需确保mock的是正确的导入路径,且返回符合hook结构的数组:

// 假设useLazyLoadUsers从'../api'导入
jest.mock('../api', () => ({
  ...jest.requireActual('../api'), // 保留其他未mock的导出
  useLazyLoadUsers: jest.fn(() => [
    jest.fn(), // mock触发函数
    { isLoading: true, data: null }
  ])
}));

it('should render the loading indicator', () => {
  render(<ComponentB />);
  expect(screen.getByText('Loading users')).toBeVisible();
});

// 测试加载完成状态
it('should render loaded state when not loading', () => {
  require('../api').useLazyLoadUsers.mockReturnValue([
    jest.fn(),
    { isLoading: false, data: [{ id: 1 }] }
  ]);
  
  render(<ComponentB />);
  expect(screen.getByText('Users loaded')).toBeVisible();
});

这种方式适合单元测试,无需依赖Store,直接测试组件是否根据hook返回的isLoading值正确渲染内容。

最佳实践总结

  • 集成测试(验证组件与Store/RTK Query的交互):优先用方案1或2,直接操作Store状态或发起真实查询请求,确保组件与状态管理的联动逻辑正确。
  • 单元测试(仅关注组件自身渲染逻辑):用方案3,mock掉hook并控制输入,测试组件输出是否符合预期。
  • 避免复制状态到其他slice,RTK Query的缓存状态可直接复用,测试时直接操作该状态即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 18:32:28