Episode ini mengajarkan testing untuk TanStack Query: QueryClient khusus test dengan retry false, testing-library dan renderHook, mock API dengan MSW, pengujian state loading success dan error, serta fake timers untuk refetchInterval.

Kode yang tidak dites adalah kode yang dianggap tidak bekerja. TanStack Query memperkenalkan beberapa tantangan testing: retry bawaan memperlambat test error, cache bisa bocor antar test, dan queryFn melakukan request jaringan sungguhan. Semua punya solusi yang sudah teruji di lapangan.
Episode 18 membahas pola testing untuk TanStack Query: QueryClient khusus test, renderHook dari testing-library, mock API dengan MSW, dan pengujian tiga state utama.
Test harus cepat dan deterministik. Retry 3 kali dengan jeda menaikkan membuat test error berjalan sangat lama, jadi matikan di konfigurasi test:
import { QueryClient } from "@tanstack/react-query"
export function createTestQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
retry: false,
gcTime: 0,
},
mutations: {
retry: false,
},
},
})
}retry: false membuat query yang gagal langsung menghasilkan error tanpa menunggu jeda. gcTime: 0 mencegah cache bocor antar test. createTestQueryClient menghasilkan instance segar per test — hindari berbagi satu QueryClient untuk semua test.
Untuk menguji hook, bungkus dengan QueryClientProvider dan gunakan renderHook:
import { renderHook, waitFor } from "@testing-library/react"
import { QueryClientProvider } from "@tanstack/react-query"
function wrapper({ children }) {
return (
<QueryClientProvider client={createTestQueryClient()}>
{children}
</QueryClientProvider>
)
}
test("useTodos menampilkan data", async () => {
const { result } = renderHook(() => useTodos(), { wrapper })
await waitFor(() => {
expect(result.current.isSuccess).toBe(true)
})
expect(result.current.data).toHaveLength(10)
})renderHook menguji hook tanpa merender komponen penuh. waitFor menunggu sampai query selesai, lalu assertion memeriksa hasilnya. Pola wrapper yang menyuntikkan QueryClient khusus test dipakai ulang di semua test.
MSW (Mock Service Worker) mencegat request jaringan di level service worker, sehingga fetch di queryFn berjalan normal tanpa kode mock:
npm install -D msw
npx msw init public/ --savenpx msw init public/ --save membuat service worker yang dipakai MSW di browser. msw diinstall sebagai dev dependency karena hanya dipakai saat test dan development.
Mock endpoint sama seperti mendefinisikan server sungguhan:
import { http, HttpResponse } from "msw"
export const handlers = [
http.get("https://jsonplaceholder.typicode.com/todos", () => {
return HttpResponse.json([{ id: 1, title: "Belajar test", completed: false }])
}),
http.get("https://jsonplaceholder.typicode.com/todos/:id", ({ params }) => {
const id = Number(params.id)
return HttpResponse.json({ id, title: `Todo ${id}`, completed: false })
}),
]http.get mendefinisikan endpoint dan respons mock. HttpResponse.json mengembalikan JSON dengan status 200. Pattern :id di URL menangkap parameter dinamis — pas dengan query ["todos", id].
Dengan MSW, kalian bisa mensimulasikan semua state: success, loading, dan error. Untuk error, kembalikan status 500:
http.get("https://jsonplaceholder.typicode.com/todos", () => {
return HttpResponse.json(
{ message: "Server error" },
{ status: 500 }
)
})HttpResponse.json dengan status: 500 membuat queryFn melempar error (jika res.ok diperiksa). Dengan retry: false, test error selesai cepat dan isError bisa diassert.
Query dengan refetchInterval menunggu waktu nyata, yang memperlambat test. Gunakan fake timers untuk mempercepat:
import { vi } from "vitest"
vi.useFakeTimers()
test("polling berjalan", async () => {
const { result } = renderHook(() => useQuery({
queryKey: ["status"],
queryFn: fetchStatus,
refetchInterval: 5000,
}), { wrapper })
await waitFor(() => expect(result.current.isSuccess).toBe(true))
const fetchCount = vi.mocked(fetchStatus).mock.calls.length
vi.advanceTimersByTime(5000)
await vi.waitFor(() => {
expect(vi.mocked(fetchStatus).mock.calls.length).toBeGreaterThan(fetchCount)
})
})vi.useFakeTimers() menggantikan timer asli, dan vi.advanceTimersByTime(5000) melompati interval 5 detik. vi.mocked memeriksa berapa kali queryFn dipanggil — bukti polling berjalan. advanceTimersByTime memberi kontrol penuh atas waktu tanpa menunggu sungguhan.
Warning
Saat memakai fake timers, panggil vi.useRealTimers() di akhir test atau di afterEach. Timer palsu yang bocor ke test lain bisa membuat waitFor menggantung tanpa batas.
Episode 18 membuat testing TanStack Query menjadi rutinitas yang ringan: QueryClient khusus test dengan retry: false, renderHook dengan wrapper, mock API MSW untuk tiga state, dan fake timers untuk polling.
Inti yang harus dibawa pulang:
retry dan set gcTime: 0 di QueryClient test.renderHook dengan provider sebagai wrapper.refetchInterval.Di episode 19 selanjutnya kita akan membahas framework adapters dan core — Vue Query, Svelte Query, Solid Query, dan Preact Query yang berbagi @tanstack/query-core, plus penggunaan QueryClient di luar framework untuk Node services dan event handlers.