MSW — API mock qilish Next.js da

~20 daq · Next.js · Testing

Muammodan boshlaylik

Ilova tashqi API larga bog'liq: valyuta kurslari, yetkazib berish xizmati, to'lov tizimi, ob-havo. Testlarda:

  • Haqiqiy API sekin va ba'zan ishlamaydi — testlar tasodifan qizil bo'ladi
  • To'lov API ga test paytida haqiqiy so'rov — xavfli va pullik
  • "API 500 qaytarsa nima bo'ladi?" — haqiqiy API ni ataylab buzib bo'lmaydi
  • Backend jamoasi API ni hali tugatmagan — frontend kutib turadi

vi.mock('./api') — har bir modul uchun alohida mock, va u fetch qanday chaqirilishini tekshirmaydi.


MSW nima?

Mock Service Worker — tarmoq darajasida so'rovlarni ushlab, soxta javob qaytaradigan kutubxona. Kodingiz odatdagidek fetch qiladi — MSW uni "havoda" ushlaydi.

Kodingiz ──fetch('https://api.kurs.uz/usd')──▶ MSW ──▶ { kurs: 12650 }
                                                (haqiqiy serverga bormaydi)
  • Node.js da (Vitest, Next.js server) — msw/node
  • Brauzerda — Service Worker orqali (msw/browser)
  • Bir xil handler lar — testlarda, development da, Storybook da

Hayotiy o'xshatma

Kino suratga olish. Haqiqiy portlash o'rniga — maxsus effektlar. Aktyor xuddi haqiqiydek harakat qiladi, kamera xuddi haqiqiydek suratga oladi — lekin hech kim xavf ostida emas va sahnani istalgancha takrorlash mumkin.


O'rnatish

npm install -D msw

Handler lar

// mocks/handlers.ts
import { http, HttpResponse, delay } from 'msw'

export const handlers = [
  http.get('https://api.kurs.uz/usd', () => {
    return HttpResponse.json({ kurs: 12650, sana: '2026-11-08' })
  }),

  http.post('https://api.yetkazish.uz/narx', async ({ request }) => {
    const { shahar } = (await request.json()) as { shahar: string }
    return HttpResponse.json({ narx: shahar === 'Toshkent' ? 25000 : 45000 })
  }),

  http.get('https://api.dokon.uz/mahsulotlar/:id', ({ params }) => {
    if (params.id === 'yoq') return HttpResponse.json({ xato: 'Topilmadi' }, { status: 404 })
    return HttpResponse.json({ id: params.id, nomi: 'Choynak', narx: 120000 })
  }),
]

Vitest da

// mocks/server.ts
import { setupServer } from 'msw/node'
import { handlers } from './handlers'

export const server = setupServer(...handlers)
// vitest.setup.ts
import '@testing-library/jest-dom/vitest'
import { beforeAll, afterEach, afterAll } from 'vitest'
import { server } from './mocks/server'

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
  • onUnhandledRequest: 'error' — mock qilinmagan so'rov ketsa, test xato beradi (tasodifan haqiqiy API ga bormaslik uchun)
  • resetHandlers — bitta testdagi o'zgarish boshqasiga ta'sir qilmasin

Test

import { it, expect } from 'vitest'
import { http, HttpResponse } from 'msw'
import { server } from '@/mocks/server'
import { somdaNarx } from '@/lib/valyuta'

it("dollar narxini so'mga o'giradi", async () => {
  expect(await somdaNarx(10)).toBe(126500)
})

it('API ishlamasa — zaxira kurs', async () => {
  server.use(
    http.get('https://api.kurs.uz/usd', () => HttpResponse.json({}, { status: 503 }))
  )
  expect(await somdaNarx(10)).toBe(125000)    // zaxira kurs 12 500
})

server.use — faqat shu test uchun handler ni almashtirish. Xato holatlari, sekin javoblar (await delay(3000)), bo'sh natijalar — hammasini oson sinash.


Next.js server tomonida — development

Server Component va Route Handler lardagi fetch lar serverda ishlaydi. Development da ularni mock qilish uchun — instrumentation.ts:

// instrumentation.ts — loyiha ildizida
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs' && process.env.API_MOCK === 'true') {
    const { server } = await import('./mocks/server')
    server.listen({ onUnhandledRequest: 'bypass' })
  }
}
API_MOCK=true npm run dev

Backend tayyor bo'lmasa ham frontend ishlay oladi.

[!INFO] Next.js App Router da MSW ni server tomonida ishlatish hali to'liq barqaror emas: hot reload paytida modul qayta yuklanishi va Next.js ning o'z fetch kengaytmalari bilan nozik muammolar uchrab turadi. Testlar uchun (Vitest) — ishonchli; development da server mock — rasmiy misollarga qarab, ehtiyotkorlik bilan.


Brauzerda

npx msw init public/ --save
// mocks/browser.ts
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'

export const worker = setupWorker(...handlers)
// app/MswProvider.tsx
'use client'

import { useEffect, useState } from 'react'

export function MswProvider({ children }: { children: React.ReactNode }) {
  const [tayyor, setTayyor] = useState(process.env.NEXT_PUBLIC_API_MOCK !== 'true')

  useEffect(() => {
    if (process.env.NEXT_PUBLIC_API_MOCK === 'true') {
      import('@/mocks/browser').then(({ worker }) =>
        worker.start({ onUnhandledRequest: 'bypass' }).then(() => setTayyor(true))
      )
    }
  }, [])

  return tayyor ? <>{children}</> : null
}

Client komponentlardagi fetch lar (TanStack Query va h.k.) mock qilinadi. DevTools → Network da [MSW] belgisi ko'rinadi.

[!WARNING] MSW ni production bundle ga qo'shmang. Uni faqat environment o'zgaruvchisi bilan va dinamik import() orqali yuklang — shunda mock kodi production ga tushmaydi.


Handler larni tartibli saqlash

mocks/
├── handlers/
│   ├── kurs.ts
│   ├── yetkazish.ts
│   └── index.ts
├── data/               ← soxta ma'lumotlar (fixtures)
├── server.ts
└── browser.ts

[!TIP] Mock javoblar haqiqiy API shakliga mos bo'lishi kerak — aks holda testlar o'tadi, production buziladi. API OpenAPI spetsifikatsiyasi bo'lsa, handler larni undan avtomatik yaratuvchi vositalar bor; yoki javob turlarini umumiy TypeScript tiplari bilan bog'lang.


Tez-tez uchraydigan xatolar

1. resetHandlers yo'q

Bitta testdagi server.use xatosi keyingi testlarga o'tadi — tasodifiy qizil testlar.

2. URL to'liq mos emas

Handler https://api.kurs.uz/usd, kod esa https://api.kurs.uz/usd/ ga so'raydi — ushlanmaydi. onUnhandledRequest: 'error' buni darhol ko'rsatadi.

3. Brauzerda mockServiceWorker.js yo'q

npx msw init public/ bajarilmagan.


Xulosa

MSW — tarmoq darajasida mock: kod odatdagidek fetch qiladi
http.get/post(url, resolver) → HttpResponse.json(data, { status })
Vitest: setupServer + listen / resetHandlers / close; onUnhandledRequest: 'error'
server.use — bitta test uchun xato, kechikish holatlari
Next.js server — instrumentation.ts (development, ehtiyotkorlik bilan)
Brauzer — setupWorker, npx msw init public/
Production ga tushmasin; mock shakli haqiqiy API ga mos

Keyingi bo'lim

65-dars: Environment Variables — .env, .env.local, .env.production

Deployment bo'limi: sozlamalar va maxfiy kalitlarni muhitlarga qarab boshqarish.