Mục lục

Integration Testing: Mocking API với MSW

Tại sao không nên mock fetch/axios thủ công? Giới thiệu MSW (Mock Service Worker) - Công cụ chặn request network để giả lập Backend hoàn hảo cho việc test.

Khi test component có gọi API, bạn không muốn gọi API thật (vì chậm, tốn tiền, data thay đổi). Bạn cần Mock.

Cách cũ: jest.spyOn(global, 'fetch'). (Dễ vỡ, phải mock từng response). Cách mới: MSW (Mock Service Worker).

1. MSW là gì?

Nó tạo ra một Server giả (Interceptor) chặn mọi request HTTP outgoing từ Tests của bạn. Component của bạn cứ gọi axios/fetch như bình thường, MSW sẽ bắt lấy và trả về JSON bạn định nghĩa.

2. Setup MSW

Cài đặt: npm install -D msw

Tạo handlers (src/mocks/handlers.ts):

ts:
import { http, HttpResponse } from 'msw';

export const handlers = [
  // Chặn GET /api/user
  http.get('/api/user', () => {
    return HttpResponse.json({
      id: '123',
      name: 'John Maverick',
    })
  }),

  // Chặn POST /api/login
  http.post('/api/login', () => {
    return HttpResponse.json({ success: true })
  }),
];

Cấu hình Vitest Server (src/mocks/server.ts):

ts:
import { setupServer } from 'msw/node';
import { handlers } from './handlers';

export const server = setupServer(...handlers);

Cấu hình vitest.setup.ts:

ts:
import { server } from './mocks/server';

beforeAll(() => server.listen()); // Bắt đầu chặn
afterEach(() => server.resetHandlers()); // Reset sau mỗi test
afterAll(() => server.close()); // Dọn dẹp

3. Viết Test Integration

tsx:
// UserProfile.test.tsx
import { render, screen, waitFor } from '@testing-library/react';
import UserProfile from './UserProfile';
// Không cần import mock api ở đây vì MSW lo rồi!

it('displays user name after fetching', async () => {
  render(<UserProfile />);

  // Ban đầu hiện loading
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  // Sau khi MSW trả data -> Hiện tên John
  await waitFor(() => {
    expect(screen.getByText('John Maverick')).toBeInTheDocument();
  });
});

4. Override Handler (Test Case Lỗi)

Đôi khi bạn muốn test trường hợp API lỗi 500.

tsx:
import { server } from '../mocks/server';
import { http, HttpResponse } from 'msw';

it('handles server error', async () => {
  // Ghi đè handler chỉ cho test case này
  server.use(
    http.get('/api/user', () => {
      return new HttpResponse(null, { status: 500 });
    })
  );

  render(<UserProfile />);
  
  await waitFor(() => {
    expect(screen.getByText(/error fetching user/i)).toBeInTheDocument();
  });
});
Quảng cáo
mdhorizontal