Belajar TanStack Query - Testing
Episode 18 of 23

Belajar TanStack Query - Testing

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.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Pendahuluan

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.

Menyiapkan QueryClient untuk Test

Konfigurasi yang Benar

Test harus cepat dan deterministik. Retry 3 kali dengan jeda menaikkan membuat test error berjalan sangat lama, jadi matikan di konfigurasi test:

JSQueryClient khusus 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.

Membungkus Hook dengan Provider

Untuk menguji hook, bungkus dengan QueryClientProvider dan gunakan renderHook:

JSTest hook dengan 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.

Mock API dengan MSW

Menginstall MSW

MSW (Mock Service Worker) mencegat request jaringan di level service worker, sehingga fetch di queryFn berjalan normal tanpa kode mock:

Install MSW
npm install -D msw
npx msw init public/ --save

npx msw init public/ --save membuat service worker yang dipakai MSW di browser. msw diinstall sebagai dev dependency karena hanya dipakai saat test dan development.

Mendefinisikan Handler

Mock endpoint sama seperti mendefinisikan server sungguhan:

JSHandler MSW
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].

Menguji Tiga State

Dengan MSW, kalian bisa mensimulasikan semua state: success, loading, dan error. Untuk error, kembalikan status 500:

JSHandler yang gagal
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.

Fake Timers untuk refetchInterval

Menguji Polling

Query dengan refetchInterval menunggu waktu nyata, yang memperlambat test. Gunakan fake timers untuk mempercepat:

JSFake timers untuk refetchInterval
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.

Penutup

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:

  • Matikan retry dan set gcTime: 0 di QueryClient test.
  • Gunakan renderHook dengan provider sebagai wrapper.
  • MSW mencegat request di level service worker.
  • Kembalikan status 500 untuk menguji state error.
  • Fake timers mempercepat test refetchInterval.
  • Selalu kembalikan timer asli di akhir test.

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.

Belajar TanStack Query - Testing | Belajar TanStack Query