DEV Community

Cover image for Hướng Dẫn Thực Tế: Cách Mock API Để Kiểm Thử Hiệu Quả
Sebastian Petrus
Sebastian Petrus

Posted on • Originally published at apidog.com

Hướng Dẫn Thực Tế: Cách Mock API Để Kiểm Thử Hiệu Quả

Các bài kiểm thử phụ thuộc vào API thật thường thất bại vì lý do không liên quan đến mã của bạn: staging down, bên thứ ba rate limit, dữ liệu bị đồng đội chỉnh sửa. Mocking API giúp loại bỏ độ mong manh đó bằng cách thay endpoint thật bằng một endpoint được kiểm soát, luôn trả về đúng phản hồi bạn cần.

Dùng thử Apidog ngay hôm nay

Hướng dẫn này đi qua quy trình thực tế để mock API phục vụ kiểm thử: định nghĩa schema, tạo phản hồi mock, chạy mock server, trỏ test vào mock, và kiểm thử các lỗi khó tái tạo bằng server thật. Ví dụ sử dụng API quản lý đơn hàng với endpoint GET /orders/{id}, nhưng quy trình áp dụng được cho hầu hết REST API hoặc GraphQL API.

Khi nào nên mock API

Mock API khi bạn muốn kiểm thử mã của mình, không phải mạng, staging hay dịch vụ bên ngoài.

Các trường hợp phù hợp:

  • Unit test cho API client.
  • Integration test giữa service và API client.
  • Kiểm thử parsing response.
  • Kiểm thử retry, timeout, fallback.
  • Kiểm thử lỗi như 404, 429, 500.

Ví dụ, bạn cần xác nhận client:

  • Parse response 200 đúng cấu trúc.
  • Retry khi gặp 503.
  • Hiển thị thông báo rõ ràng khi gặp 404.
  • Backoff đúng khi bị 429.

Không trường hợp nào bắt buộc phải gọi server thật.

Ngược lại, vẫn nên giữ một lớp test nhỏ gọi API thật cho contract test và end-to-end test. Các test này đảm bảo mock vẫn khớp với thực tế. Cách chia hợp lý là:

  • Mock để có tốc độ, tính ổn định và khả năng cô lập.
  • Gọi API thật để xác nhận hợp đồng.

Bạn có thể xem thêm về các kịch bản mà việc mock API mang lại hiệu quả và sự khác biệt giữa máy chủ mock và máy chủ thật.

Quy trình 5 bước để mock API cho kiểm thử

Một workflow mock API thường gồm 5 bước:

  1. Định nghĩa schema để mock phản ánh đúng cấu trúc response thật.
  2. Tạo phản hồi mock, tĩnh hoặc động, từ schema đó.
  3. Chạy mock server để phục vụ response tại một URL.
  4. Trỏ test vào mock bằng cách cấu hình base URL.
  5. Kiểm thử các trường hợp lỗi mà server thật khó tạo ra theo yêu cầu.

Ví dụ xuyên suốt trong bài là endpoint:

GET /orders/{id}
Enter fullscreen mode Exit fullscreen mode

Bước 1: Định nghĩa schema

Mock chỉ hữu ích nếu nó phản ánh đúng hợp đồng API thật. Nếu API đã có tài liệu OpenAPI, hãy dùng trực tiếp. Nếu chưa có, hãy viết schema tối thiểu cho endpoint đang cần test.

Ví dụ schema rút gọn cho GET /orders/{id}:

paths:
  /orders/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  status: { type: string, enum: [pending, shipped, delivered] }
                  total: { type: number }
                  items: { type: array, items: { type: object } }
        '404':
          description: Order not found
Enter fullscreen mode Exit fullscreen mode

Schema này có hai vai trò:

  • Cho mock biết response cần có những field nào.
  • Là nguồn sự thật duy nhất cho cả backend, mock và test.

Khi backend thay đổi hợp đồng, bạn cập nhật schema. Mock được tạo từ schema sẽ bám theo thay đổi đó, thay vì bị lệch âm thầm. Đây là nền tảng giúp kiểm thử hợp đồng API đáng tin cậy.

Bước 2: Tạo phản hồi mock

Bạn có hai kiểu response mock phổ biến: tĩnh và động.

Phản hồi mock tĩnh

Phản hồi tĩnh là payload JSON cố định. Mock server luôn trả về cùng một response.

Ví dụ:

{
  "id": "order_8842",
  "status": "shipped",
  "total": 149.99,
  "items": [
    {
      "sku": "sku_123",
      "quantity": 1
    },
    {
      "sku": "sku_456",
      "quantity": 2
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Phù hợp cho:

  • Unit test cần kết quả dự đoán được.
  • Test logic parse response.
  • Test UI state với dữ liệu cố định.
  • Test assertion chi tiết.

Phản hồi mock động

Phản hồi động được tạo theo từng request. Thay vì hard-code "total": 149.99, mock có thể tạo:

  • UUID cho id.
  • Một giá trị enum hợp lệ cho status.
  • Một số tiền hợp lý cho total.
  • Danh sách items có độ dài thay đổi.

Dữ liệu động giúp bắt lỗi mà một payload cố định có thể che giấu, ví dụ:

  • Parser lỗi với chuỗi dài.
  • Client không xử lý null.
  • UI vỡ layout với danh sách nhiều item.
  • Logic sai khi gặp enum ít phổ biến.

Thông thường, nên dùng cả hai:

  • Payload tĩnh cho test cần assertion chính xác.
  • Payload động cho coverage rộng hơn.

Với Apidog, bạn có thể tạo mock từ schema mà không cần viết thủ công toàn bộ response. Apidog đọc schema và tự động tạo mock endpoint, đồng thời map các field như email, phone, avatar sang kiểu dữ liệu phù hợp.

Khi viết payload thủ công, hãy giữ dữ liệu thực tế. Một order test với "total": 0items: [] có thể giúp test pass nhưng lại che giấu bug. Nên dùng dữ liệu gần với production:

  • Tổng tiền có ý nghĩa.
  • Hai hoặc ba item.
  • status hợp lệ trong enum.
  • Field optional xuất hiện trong một số case.

Bước 3: Chạy mock server

Response mock chỉ hữu ích khi có server phục vụ nó. Có hai lựa chọn chính: local mock server và cloud mock server.

Local mock server

Local mock server chạy trên máy của bạn, ví dụ:

http://127.0.0.1:4010
Enter fullscreen mode Exit fullscreen mode

Phù hợp cho:

  • Unit test.
  • Integration test.
  • Chạy test trong local development.
  • Chạy test trong CI nếu mock server được start cùng pipeline.

Các công cụ nhẹ như Prism có thể chạy mock server trực tiếp từ file OpenAPI:

prism mock openapi.yaml
# Mock server listening on http://127.0.0.1:4010
Enter fullscreen mode Exit fullscreen mode

Cloud mock server

Cloud mock server cung cấp URL công khai. Dùng khi:

  • Mobile app cần gọi mock từ thiết bị thật.
  • CI runner không truy cập được máy local.
  • Đối tác bên ngoài cần thử API trước khi backend hoàn tất.
  • Team nhiều người muốn dùng chung một mock endpoint.

Apidog cung cấp Mock Cloud URL cho từng project, giúp các thành viên trong team truy cập cùng một mock endpoint mà không phụ thuộc vào máy cá nhân.

Với test tự động, ưu tiên local mock server vì:

  • Nhanh hơn.
  • Không có network latency bên ngoài.
  • Không chia sẻ state giữa các build.
  • Ít flaky hơn.

Cloud mock phù hợp hơn cho demo, test đa thiết bị và cộng tác.

Bước 4: Trỏ test vào mock

Không hard-code production URL trong test. Cách sạch hơn là truyền base URL qua biến môi trường.

Ví dụ với JavaScript:

// orderClient.test.js
import { getOrder } from './orderClient.js';

const BASE_URL = process.env.API_BASE_URL || 'http://127.0.0.1:4010';

test('parses a shipped order', async () => {
  const order = await getOrder('order_8842', BASE_URL);

  expect(order.status).toBe('shipped');
  expect(typeof order.total).toBe('number');
});
Enter fullscreen mode Exit fullscreen mode

Client nên nhận base URL từ bên ngoài:

// orderClient.js
export async function getOrder(id, baseUrl) {
  const response = await fetch(`${baseUrl}/orders/${id}`);

  if (!response.ok) {
    throw new Error(`Failed to fetch order: ${response.status}`);
  }

  return response.json();
}
Enter fullscreen mode Exit fullscreen mode

Cách này giúp cùng một test có thể chạy với:

API_BASE_URL=http://127.0.0.1:4010 npm test
Enter fullscreen mode Exit fullscreen mode

Hoặc chạy contract test với API thật:

API_BASE_URL=https://api.example.com npm test
Enter fullscreen mode Exit fullscreen mode

Trong CI, bạn chỉ cần start mock server trước, sau đó set API_BASE_URL trỏ vào mock. Nếu pipeline đã có bước kiểm thử API, nguyên tắc tương tự cũng áp dụng khi bạn tự động hóa các bài kiểm thử API trong CI/CD.

Bước 5: Kiểm thử các trường hợp lỗi

Đây là phần nhiều team bỏ qua, nhưng lại là nơi mocking mang lại giá trị lớn nhất.

Server thật hiếm khi trả về 500 đúng lúc bạn cần test. Mock server thì có thể.

Hãy cấu hình mock để trả về các lỗi cụ thể và xác nhận client xử lý đúng:

Kịch bản Mock trả về Điều cần xác nhận
Bản ghi không tồn tại 404 Client đưa ra lỗi “không tìm thấy” rõ ràng
Lỗi máy chủ 500 Client retry, sau đó hiển thị fallback
Bị giới hạn tốc độ 429 kèm Retry-After Client backoff đúng
Phản hồi chậm 200 sau 5 giây trễ Client timeout và phục hồi
Body lỗi định dạng 200 với JSON bị hỏng Client xử lý lỗi, không crash

Ví dụ test cho 404:

test('throws not found error when order does not exist', async () => {
  await expect(getOrder('order_404', BASE_URL)).rejects.toThrow(
    'Failed to fetch order: 404'
  );
});
Enter fullscreen mode Exit fullscreen mode

Ví dụ test timeout:

test('times out when API is too slow', async () => {
  const controller = new AbortController();

  const timeout = setTimeout(() => {
    controller.abort();
  }, 1000);

  await expect(
    fetch(`${BASE_URL}/orders/order_slow`, {
      signal: controller.signal
    })
  ).rejects.toThrow();

  clearTimeout(timeout);
});
Enter fullscreen mode Exit fullscreen mode

Các quy tắc mock nâng cao của Apidog cho phép trả về response khác nhau dựa trên request. Ví dụ:

  • GET /orders/order_404 trả về 404.
  • GET /orders/order_rate_limited trả về 429.
  • Các ID còn lại trả về 200.

Như vậy, một mock endpoint có thể bao phủ cả happy path và error path. Kết hợp với các xác nhận API, test của bạn sẽ kiểm tra hành vi, không chỉ status code.

Tổ chức mock khi test suite lớn dần

Một mock endpoint thì dễ quản lý. Hàng trăm mock endpoint trong nhiều test suite sẽ nhanh chóng mất kiểm soát nếu không có quy ước.

Áp dụng các nguyên tắc sau:

Nhóm mock theo service thật

Không nhóm mock theo từng test file. Hãy nhóm theo API/service mà chúng đại diện.

Ví dụ:

mocks/
  orders/
    order-shipped.json
    order-not-found.json
    order-rate-limited.json
  payments/
    payment-success.json
    payment-declined.json
Enter fullscreen mode Exit fullscreen mode

Khi API đơn hàng thay đổi, bạn chỉ cần cập nhật trong mocks/orders.

Đặt tên theo kịch bản

Tên mock nên mô tả hành vi:

  • order-shipped
  • order-pending
  • order-not-found
  • order-rate-limited
  • order-malformed-response

Tên tốt giúp test failure dễ đọc hơn.

Version mock cùng test

Mock là một phần của test, nên cần được lưu trong version control cùng test code. Khi review pull request, reviewer nên kiểm tra cả:

  • Schema có đổi không.
  • Mock tương ứng đã cập nhật chưa.
  • Test có bao phủ error case mới không.

Tránh copy payload cho từng test

Đừng tạo một JSON gần giống nhau cho mỗi test. Thay vào đó, định nghĩa một base response rồi override field cần test.

Ví dụ:

const baseOrder = {
  id: 'order_8842',
  status: 'shipped',
  total: 149.99,
  items: [
    { sku: 'sku_123', quantity: 1 },
    { sku: 'sku_456', quantity: 2 }
  ]
};

const pendingOrder = {
  ...baseOrder,
  status: 'pending'
};

const deliveredOrder = {
  ...baseOrder,
  status: 'delivered'
};
Enter fullscreen mode Exit fullscreen mode

Cách này giúp giảm trùng lặp và tránh phải cập nhật nhiều file khi hợp đồng API thay đổi. Kỷ luật tương tự giúp bộ kiểm thử tự động hóa API dễ bảo trì hơn.

Giữ mock luôn khớp với thực tế

Mock có thể bị lệch khỏi backend. Ví dụ:

  • Backend thêm field mới.
  • Backend đổi total thành amount.
  • Enum status có thêm giá trị mới.
  • Field trước đây optional trở thành required.

Nếu mock vẫn trả về cấu trúc cũ, test vẫn xanh nhưng production có thể lỗi. Đây là rủi ro lớn nhất khi mock API.

Để giảm rủi ro, hãy áp dụng hai thói quen.

1. Tạo mock từ cùng schema mà backend dùng

Nếu backend công bố OpenAPI spec, hãy dùng chính spec đó để tạo mock.

Một mock sinh từ OpenAPI có thể được cập nhật khi schema đổi. Một mock viết tay thường bị quên.

2. Chạy contract test với API thật

Duy trì một bộ test nhỏ gọi API thật theo lịch trình hoặc trong pipeline riêng. Nhiệm vụ của chúng là kiểm tra response thật vẫn khớp với schema.

Ví dụ chiến lược:

  • Pull request: chạy unit/integration test với mock.
  • Hằng ngày hoặc trước release: chạy contract test với API thật.
  • Khi contract test fail: cập nhật schema, backend hoặc mock.

Ngoài ra, hãy review mock trong code review. Nếu pull request thay đổi response API, mock tương ứng cũng cần được cập nhật. Coi mock là một phần của hợp đồng API, không phải dữ liệu test dùng một lần.

Nếu bạn muốn một môi trường duy nhất để lưu schema, tạo mock và chạy test dựa trên schema đó, hãy Tải xuống Apidog. Apidog giúp đồng bộ thiết kế API, mock server và test suite để mock luôn bám theo hợp đồng hiện tại. Bạn cũng có thể tham khảo thêm danh sách các công cụ mock API REST.

Câu hỏi thường gặp

Tôi có nên mock API cho mọi bài kiểm thử không?

Không. Hãy mock cho unit test và integration test khi bạn đang kiểm tra mã của mình. Vẫn nên giữ một bộ nhỏ contract test và end-to-end test gọi API thật để đảm bảo mock không lệch khỏi production.

Phản hồi mock tĩnh và động khác nhau thế nào?

Phản hồi tĩnh là payload JSON cố định, phù hợp cho test cần kết quả dự đoán được. Phản hồi động được tạo theo từng request với dữ liệu thực tế hơn, giúp phát hiện lỗi mà một payload cố định có thể bỏ sót. Hầu hết team nên dùng cả hai.

Làm sao để đảm bảo mock luôn chính xác?

Tạo mock từ cùng schema mà backend sử dụng, lý tưởng nhất là OpenAPI. Sau đó chạy contract test định kỳ với API thật để xác nhận response production vẫn khớp schema. Nếu contract test fail, mock hoặc backend cần được cập nhật.

Mock có thể mô phỏng response chậm hoặc lỗi không?

Có. Đây là một trong những lý do quan trọng để mock API. Bạn có thể cấu hình mock trả về 500, 429 kèm Retry-After, JSON lỗi định dạng, hoặc response 200 bị delay. Nhờ đó bạn kiểm thử được retry, timeout và fallback mà server thật khó tạo ra theo yêu cầu.

Nên dùng local mock server hay cloud mock server để kiểm thử?

Dùng local mock server cho test tự động vì nhanh, ít flaky và không chia sẻ state giữa các build. Dùng cloud mock server khi mobile app, CI runner hoặc đối tác bên ngoài cần truy cập mock mà không phụ thuộc vào máy local.

Top comments (0)