# MSW (Mock Service Worker) 2026: คู่มือ API Mocking Frontend Testing สำหรับ SME ไทย
ทีม Frontend ส่วนใหญ่เคยเจอปัญหานี้ — Backend API ยังไม่พร้อม แต่ต้องเริ่มทำ UI ให้ทัน หรือเวลาเขียน Unit Test ต้อง mock fetch แต่ละจุดด้วย jest.mock จนโค้ดเทสเปราะบาง พอเปลี่ยน endpoint ทีนึงต้องไล่แก้ทั้งโปรเจกต์
MSW (Mock Service Worker) คือคำตอบที่ทีมพัฒนาเลือกใช้เป็นมาตรฐานในปี 2026 เพราะมัน intercept request ระดับ network layer ไม่ใช่แค่ mock function ทำให้โค้ด production และโค้ดที่ใช้ตอน test เหมือนกัน 100% ผลคือเทสที่เชื่อถือได้และ developer experience ที่ดีกว่า fixture แบบเดิมอย่างชัดเจน
บทความนี้จะอธิบาย MSW ตั้งแต่หลักการทำงาน วิธี setup กับ Next.js 15 + Vitest พร้อมตัวอย่างโค้ดจริงที่ทีม Dev ใช้ในโปรเจกต์ B2B และ SaaS ของลูกค้า ADS FIT
MSW คืออะไร และทำไมต้องใช้
MSW เป็น library ที่ใช้ Service Worker API ของ browser และ request interceptor ของ Node.js เพื่อ intercept HTTP request ก่อนที่มันจะออกไปยัง network จริง แล้วส่ง response ที่เรากำหนดกลับมา
ต่างจากการใช้ jest.mock หรือ axios-mock-adapter ตรงที่ โค้ดของแอปไม่ต้องรู้ว่ากำลัง test อยู่ — ยังคง call fetch หรือ axios แบบปกติ MSW จะดักจับให้เอง
ข้อดีหลัก ๆ:
เปรียบเทียบ MSW กับวิธี Mock แบบอื่น
| คุณสมบัติ | MSW | jest.mock / vi.mock | json-server | axios-mock-adapter |
|-----------|-----|---------------------|-------------|---------------------|
| Layer ที่ทำงาน | Network | Module | HTTP server แยก | HTTP client เฉพาะ |
| ใช้ใน Browser ได้ | ใช่ (Service Worker) | ไม่ | ใช่ | ใช่ |
| ใช้ใน Node ได้ | ใช่ | ใช่ | ใช่ | ใช่ |
| Reuse handler ระหว่าง dev/test | ได้ | ไม่ได้ | ได้ | ไม่ได้ |
| รองรับ GraphQL | ได้ | manual | ไม่ได้ | ไม่ได้ |
| Setup ครั้งเดียว ใช้ได้ทุก HTTP client | ใช่ | ไม่ | ใช่ | ไม่ |
ติดตั้งและตั้งค่า MSW กับ Next.js + Vitest
Step 1: ติดตั้ง package
```bash
pnpm add -D msw @mswjs/data
npx msw init public/ --save
```
คำสั่ง `msw init` จะสร้าง `mockServiceWorker.js` ในโฟลเดอร์ public/ ใช้สำหรับ intercept request ฝั่ง browser
Step 2: สร้าง handler
สร้างไฟล์ `src/mocks/handlers.ts`
```ts
import { http, HttpResponse } from 'msw'
export const handlers = [
http.get('/api/orders', () => {
return HttpResponse.json([
{ id: 'ORD-001', total: 1290, status: 'paid' },
{ id: 'ORD-002', total: 540, status: 'pending' },
])
}),
http.post('/api/orders', async ({ request }) => {
const body = await request.json()
return HttpResponse.json(
{ id: 'ORD-NEW', ...body, status: 'pending' },
{ status: 201 }
)
}),
http.get('/api/orders/:id', ({ params }) => {
if (params.id === 'unknown') {
return new HttpResponse(null, { status: 404 })
}
return HttpResponse.json({ id: params.id, total: 1290 })
}),
]
```
Step 3: ตั้งค่า browser worker
ไฟล์ `src/mocks/browser.ts`
```ts
import { setupWorker } from 'msw/browser'
import { handlers } from './handlers'
export const worker = setupWorker(...handlers)
```
Step 4: ตั้งค่า Node server สำหรับ Vitest
ไฟล์ `src/mocks/server.ts`
```ts
import { setupServer } from 'msw/node'
import { handlers } from './handlers'
export const server = setupServer(...handlers)
```
ไฟล์ `vitest.setup.ts`
```ts
import { afterAll, afterEach, beforeAll } from 'vitest'
import { server } from './src/mocks/server'
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
afterEach(() => server.resetHandlers())
afterAll(() => server.close())
```
Step 5: เปิด worker ตอน Development
ใน Next.js App Router สร้าง `src/mocks/index.ts`
```ts
export async function enableMocking() {
if (process.env.NEXT_PUBLIC_API_MOCKING !== 'enabled') return
if (typeof window === 'undefined') {
const { server } = await import('./server')
server.listen()
} else {
const { worker } = await import('./browser')
await worker.start()
}
}
```
แล้วเรียกใน root layout หรือ providers ก่อน render UI
เขียน Test ด้วย Vitest + React Testing Library
```ts
import { render, screen } from '@testing-library/react'
import { http, HttpResponse } from 'msw'
import { server } from '@/mocks/server'
import OrdersPage from '@/app/orders/page'
describe('OrdersPage', () => {
it('แสดงรายการ orders ที่ดึงจาก API', async () => {
render(<OrdersPage />)
expect(await screen.findByText('ORD-001')).toBeInTheDocument()
})
it('แสดง error เมื่อ API ตอบ 500', async () => {
server.use(
http.get('/api/orders', () =>
new HttpResponse(null, { status: 500 })
)
)
render(<OrdersPage />)
expect(await screen.findByText(/เกิดข้อผิดพลาด/i)).toBeInTheDocument()
})
})
```
จุดที่น่าสนใจคือ `server.use()` ใช้ override handler เฉพาะ test case ทำให้เทส edge case อย่าง 401, 429, 500 หรือ timeout ได้ง่าย ๆ โดยไม่ต้องแก้ handler หลัก
Best Practice สำหรับทีม SME ไทย
หลังจากใช้ MSW มากับโปรเจกต์ลูกค้าหลายราย ทีม ADS FIT สรุปแนวทางที่เวิร์กที่สุดได้ดังนี้:
ผลลัพธ์ที่ทีมได้รับจริง
จากการช่วยลูกค้า SaaS ภาคบริการของ ADS FIT migrate ไป MSW ในไตรมาส 1/2026 ทีม Frontend รายงานตัวเลขเหล่านี้
สรุปและ CTA
MSW เป็น มาตรฐานปี 2026 สำหรับการ mock API ใน Frontend ทั้งระดับ Unit Test, Component Test และ E2E เพราะมัน intercept ที่ network layer ทำให้โค้ด production กับโค้ด test เหมือนกัน เทสที่ผ่านบน MSW จึงสะท้อนพฤติกรรมจริงของแอป
สำหรับทีม SME ไทยที่ใช้ Next.js, React, Vitest หรือ Playwright การลงทุน 1-2 sprint เพื่อ migrate ไป MSW จะคืนทุนภายในไตรมาสเดียว ทั้งจากความเร็วในการพัฒนาและคุณภาพโค้ดที่ดีขึ้น
หากทีมของคุณกำลังเริ่มโปรเจกต์ Next.js ใหม่ หรืออยากปรับปรุง testing pipeline ที่มีอยู่ — ทีม ADS FIT ยินดีให้คำปรึกษา ออกแบบ architecture และ pair programming ติดต่อเราได้ผ่านหน้า Contact หรืออ่านบทความ Development อื่น ๆ ที่ blog ของเรา
