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

如何测试@tanstack/react-query v5的useSuspenseQuery Hook?

解决useSuspenseQuery单元测试加载/错误场景失败问题

核心问题分析

useSuspenseQuery和useQuery的核心行为差异在于:它会在加载阶段直接抛出Promise,在请求失败时直接抛出错误对象,而非像useQuery那样返回isLoading/error状态。你的测试失败是因为没有正确处理这些抛出行为,且组件包裹顺序存在问题。


代码修改方案

1. 调整组件包裹顺序

你的QueryWrapperWithSuspense中,Suspense包裹了QueryClientProvider,这会导致QueryClient在组件暂停后才初始化,引发状态异常。需要将QueryClientProvider放在外层:

export const QueryWrapperWithSuspense = ({ children }: PropsWithChildren) => {
  const queryClient = new QueryClient({
    ...queryClientConfig,
    defaultOptions: {
      queries: {
        retry: 0, // 测试时关闭重试,避免等待多次重试逻辑
      },
    },
  })
  return (
    <QueryClientProvider client={queryClient}>
      <Suspense fallback={<div>Loading</div>}>
        {children}
      </Suspense>
    </QueryClientProvider>
  )
}

2. 加载场景测试代码调整

使用renderHook时需开启suspense选项,通过waitForNextUpdate等待Promise解决:

import { renderHook, waitForNextUpdate } from '@testing-library/react'
import { useGetData } from './hook'
import { QueryWrapperWithSuspense } from './wrapper'
import { rest } from 'msw'
import { setupServer } from 'msw/node'

const server = setupServer(
  rest.get('/api/data', (req, res, ctx) => {
    return res(ctx.delay(100), ctx.json({ data: 'test' }))
  })
)

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('加载阶段正确触发Suspense', async () => {
  const { result, waitForNextUpdate } = renderHook(() => useGetData(), {
    wrapper: QueryWrapperWithSuspense,
    suspense: true, // 必须启用该选项处理Suspense抛出的Promise
  })

  // 初始阶段hook抛出Promise,result.current为null是预期行为
  expect(result.current).toBeNull()

  // 等待Suspense恢复
  await waitForNextUpdate()

  // 加载完成后验证数据
  expect(result.current.data).toEqual({ data: 'test' })
})

3. 错误场景测试代码调整

需要捕获useSuspenseQuery抛出的错误,而非检查result.current.error:

test('请求失败时正确抛出错误', async () => {
  server.use(
    rest.get('/api/data', (req, res, ctx) => {
      return res(ctx.status(500), ctx.json({ message: 'Server Error' }))
    })
  )

  const { result, waitForNextUpdate } = renderHook(() => useGetData(), {
    wrapper: QueryWrapperWithSuspense,
    suspense: true,
  })

  try {
    await waitForNextUpdate()
    // 如果未抛出错误,测试失败
    expect(true).toBe(false)
  } catch (error) {
    // 验证抛出的错误内容(根据你的API错误格式调整)
    expect((error as Error).message).toBe('Server Error')
  }
})

关键注意事项

  • useSuspenseQuery不会返回isLoading/error状态,所有异步状态都通过抛出行为传递,测试时必须对应处理。
  • 测试时关闭重试逻辑(retry:0),避免因重试导致测试超时或结果混乱。
  • 确保QueryClientProvider在Suspense外层,保证QueryClient在组件暂停前完成初始化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:58:19