测试
Solid Query 通过基础原语(primitives)工作——这些原语要么是我们提供的,要么是你编写的、封装了这些原语的自定义原语。
这些原语从 QueryClientProvider 中读取 QueryClient,因此必须在 QueryClientProvider 下运行。你可以使用 Solid Testing Library 提供的 renderHook 来编写它们的单元测试。
通过运行以下命令安装:
1npm install @solidjs/testing-library --save-dev我们的第一项测试
安装完成后,即可编写一个简单测试。假设有如下自定义原语:
1export function useCustomQuery() {2 return useQuery(() => ({ queryKey: ['customQuery'], queryFn: () => 'Hello' }))3}我们可以为其编写如下测试:
1import { renderHook, waitFor } from '@solidjs/testing-library'2import { QueryClient, QueryClientProvider } from '@tanstack/solid-query'3import type { ParentProps } from 'solid-js'4 5const queryClient = new QueryClient()6const wrapper = (props: ParentProps) => (7 <QueryClientProvider client={queryClient}>8 {props.children}9 </QueryClientProvider>10)11 12const { result } = renderHook(() => useCustomQuery(), { wrapper })13 14await waitFor(() => expect(result.isSuccess).toBe(true))15 16expect(result.data).toEqual('Hello')注意,我们提供了一个自定义包装器,用于构建 QueryClient 和 QueryClientProvider。这有助于确保我们的测试完全与其他测试隔离。
该包装器可以只编写一次,但若如此,则需确保每次测试前都清空 QueryClient,且测试不能并行执行;否则,一个测试会影响其他测试的结果。
result 是 useQuery 返回的响应式对象,而非快照,因此应在等待之后再读取其属性,而不是在最开始就对其进行解构。解构所得的值会保持其被读取时的值。
waitFor 会重试断言直至其通过,这对等待真实请求的查询函数有效。当查询函数等待定时器时,你可以改用模拟定时器(fake timers)并手动推进它们,例如在 Vitest 中使用 vi.advanceTimersByTimeAsync。请在 afterEach 中恢复真实定时器,以确保即使断言失败,定时器也能被正确恢复:
1beforeEach(() => {2 vi.useFakeTimers()3})4 5afterEach(() => {6 vi.useRealTimers()7})8 9it('should resolve the delayed query', async () => {10 const { result } = renderHook(11 () =>12 useQuery(() => ({13 queryKey: ['delayed'],14 queryFn: () =>15 new Promise((resolve) => setTimeout(() => resolve('Hello'), 1000)),16 })),17 { wrapper },18 )19 20 await vi.advanceTimersByTimeAsync(1000)21 22 expect(result.data).toEqual('Hello')23})对于更复杂的原语,直接测试使用它们的组件可能更简便:使用 Solid Testing Library 的 render 在相同的 QueryClientProvider 下渲染该组件,并对其渲染结果进行断言。
关闭重试
该库默认启用三次指数退避重试(retry),这意味着如果你希望测试一个出错的查询,测试很可能会超时。最简单的禁用重试方式是通过你传给 QueryClientProvider 的 QueryClient 进行配置。我们来扩展上面的例子:
1const queryClient = new QueryClient({2 defaultOptions: {3 queries: {4 // ✅ turns retries off5 retry: false,6 },7 },8})9const wrapper = (props: ParentProps) => (10 <QueryClientProvider client={queryClient}>11 {props.children}12 </QueryClientProvider>13)这会将组件树中所有查询的默认重试次数设为“零次”。需要注意的是,该设置仅在你的实际 useQuery 未显式指定重试次数时才生效;如果某个查询明确设置了 5 次重试,该设置仍将优先于默认值,因为默认值仅作为后备方案使用。
使用 Jest 将 gcTime 设为 Infinity
如果你使用 Jest,可将 gcTime 设为 Infinity,以避免出现 “Jest did not exit one second after the test run completed” 错误提示。这是服务端的默认行为;仅当你显式设置了 gcTime 时,才需要手动设置此项。