如何测试@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
相关产品推荐
相关产品推荐

