Các bài kiểm thử phụ thuộc vào API trực tiếp là các bài kiểm thử thất bại vì những lý do không chính đáng. Một máy chủ staging ngừng hoạt động, giới hạn tốc độ của bên thứ ba được kích hoạt, một đồng đội thay đổi một bản ghi, và đột nhiên bộ kiểm thử của bạn báo lỗi mặc dù mã của bạn vẫn ổn. Mocking API loại bỏ sự mong manh đó. Bạn thay thế điểm cuối thật bằng một đối tượng thay thế được kiểm soát, trả về chính xác phản hồi mà bạn yêu cầu, mọi lúc.
Hướng dẫn này sẽ chỉ ra các bước thực tế để mock một API cho mục đích kiểm thử. Bạn sẽ định nghĩa một lược đồ (schema), tạo các phản hồi mock, hướng mã kiểm thử của bạn đến mock, và bao quát các trường hợp lỗi mà một máy chủ thực hiếm khi tạo ra theo yêu cầu. Các ví dụ sử dụng một API quản lý đơn hàng nhỏ, nhưng quy trình làm việc này có thể áp dụng cho bất kỳ dịch vụ REST hoặc GraphQL nào.
Khi việc mock API là lựa chọn đúng đắn
Hãy mock API khi điều bạn muốn kiểm thử là mã của chính bạn, chứ không phải mạng lưới. Các bài kiểm thử đơn vị và hầu hết các bài kiểm thử tích hợp đều thuộc loại này. Bạn muốn biết rằng client của mình phân tích cú pháp mã 200 một cách chính xác, thử lại khi gặp 503, và hiển thị thông báo rõ ràng khi gặp 404. Không có điều nào trong số đó yêu cầu một máy chủ thực.
Hãy giữ lại API thật cho các bài kiểm thử hợp đồng (contract tests) và một lớp mỏng các bài kiểm tra end-to-end. Những bài kiểm thử đó tồn tại để xác nhận rằng mock vẫn khớp với thực tế. Nếu bạn mock mọi thứ và không bao giờ xác minh với dịch vụ trực tiếp, bộ kiểm thử của bạn vẫn xanh trong khi môi trường sản phẩm bị lỗi. Việc phân chia đại khái là: mock để tăng tốc độ và sự cô lập, gọi API thật để xác nhận hợp đồng. Để tìm hiểu sâu hơn về vị trí của từng loại, hãy xem phân tích này 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 làm việc năm bước tổng quan
Việc mock một API để kiểm thử luôn bao gồm năm bước giống nhau, bất kể ngôn ngữ hay framework nào:
- Định nghĩa lược đồ (schema) để mock phản ánh đúng cấu trúc phản hồi thực.
- Tạo các phản hồi mock, tĩnh hoặc động, từ lược đồ đó.
- Chạy một máy chủ mock để phục vụ các phản hồi đó tại một URL.
- Hướng các bài kiểm thử của bạn đến mock bằng cách cấu hình URL cơ sở.
- Kiểm thử các trường hợp lỗi mà máy chủ thật sẽ không tạo ra theo yêu cầu.
Phần còn lại của hướng dẫn này sẽ đi qua từng bước với một ví dụ cụ thể: một API quản lý đơn hàng nhỏ với điểm cuối GET /orders/{id}. Hãy ghi nhớ điểm cuối đó như một chủ đề xuyên suốt.
Bước 1: Định nghĩa lược đồ (schema)
Một mock chỉ hữu ích nếu nó phản ánh đúng cấu trúc phản hồi thực. Hãy bắt đầu từ một lược đồ. Nếu API của bạn đã có tài liệu OpenAPI, hãy sử dụng nó. Nếu không, hãy viết một cái cho điểm cuối đang được kiểm thử. Dưới đây là một định nghĩa 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
Lược đồ này có hai nhiệm vụ. Nó cho mock biết những trường nào cần trả về, và cung cấp cho bạn một nguồn sự thật duy nhất. Khi backend thay đổi hợp đồng, bạn cập nhật lược đồ và mọi mock được tạo ra từ đó vẫn chính xác. Mocking ưu tiên lược đồ là yếu tố 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 cách để tạo phần thân phản hồi.
Các phản hồi tĩnh là JSON cố định. Bạn viết một payload chính xác một lần và mock trả về nó không thay đổi. Chúng có thể dự đoán được và dễ dàng để kiểm tra, điều này khiến chúng lý tưởng cho các bài kiểm thử đơn vị nơi bạn muốn một giá trị đã biết.
Các phản hồi động được tạo ra theo từng yêu cầu. Thay vì mã hóa cứng "total": 149.99, mock điền vào các trường với các giá trị được tạo ra thực tế: một UUID cho id, một thành viên enum ngẫu nhiên cho status, một số tiền tệ hợp lý cho total. Dữ liệu động bắt được các lỗi mà một payload cố định duy nhất có thể che giấu, như một bộ phân tích cú pháp bị lỗi trên các chuỗi dài hoặc các trường null không mong muốn.
Hầu hết các nhóm đều sử dụng cả hai. Payload tĩnh cho các bài kiểm thử nặng về xác nhận, tạo động cho phạm vi phủ sóng kiểu fuzz. Với Apidog, bạn không cần tự tay viết bất kỳ loại nào. Apidog đọc lược đồ và tự động tạo một điểm cuối mock, khớp các tên trường như email, phone hoặc avatar với kiểu dữ liệu chính xác. Bạn chỉ cần trỏ trình duyệt đến URL mock và nhận được phản hồi hợp lệ ngay lập tức.
Khi bạn tự tay viết payload, hãy giữ chúng thật thực tế. Một đơn đặt hàng kiểm thử với "total": 0 và một mảng items trống có thể vượt qua một bộ phân tích cú pháp đơn giản nhưng lại che giấu lỗi. Hãy sử dụng các giá trị giống như trong môi trường sản phẩm: một tổng số trông thật, hai hoặc ba mặt hàng, một trạng thái thực sự có trong enum. Dữ liệu mock càng gần với những gì một yêu cầu thực trả về, bài kiểm thử của bạn càng có giá trị.
Bước 3: Chạy máy chủ mock
Một phản hồi mock không có tác dụng gì cho đến khi có thứ gì đó phục vụ nó. Bạn có hai lựa chọn lưu trữ.
Một máy chủ mock cục bộ chạy trên máy của bạn, thường trên một cổng như localhost:4010. Nó nhanh, hoạt động ngoại tuyến và là mặc định cho các bài kiểm thử đơn vị và tích hợp. Các công cụ nhẹ như Prism có thể khởi động một máy chủ trực tiếp từ một tệp OpenAPI:
prism mock openapi.yaml
# Mock server listening on http://127.0.0.1:4010
Một máy chủ mock trên đám mây có một URL công khai. Hãy sử dụng nó khi một ứng dụng di động, một CI runner, hoặc một đối tác bên ngoài cần gọi mock mà không có quyền truy cập vào máy tính xách tay của bạn. Apidog cung cấp cho mỗi dự án một URL Mock Cloud được lưu trữ, vì vậy một đồng đội trên mạng khác có thể truy cập cùng một điểm cuối mà bạn đang sử dụng.
Đối với các lần chạy kiểm thử, hãy ưu tiên máy chủ cục bộ. Nó không có độ trễ mạng và không có trạng thái chia sẻ, vì vậy hai bản dựng sẽ không bao giờ xung đột. Sử dụng tùy chọn đám mây cho các bản demo và kiểm thử đa thiết bị.
Bước 4: Hướng các bài kiểm thử của bạn đến mock
Bây giờ, hãy kết nối mã kiểm thử với mock thay vì môi trường sản phẩm. Cách tiếp cận sạch nhất là một URL cơ sở có thể cấu hình được. Hãy đọc nó từ một biến môi trường để cùng một tệp kiểm thử có thể chạy với mock cục bộ và API thật trong một tác vụ kiểm thử hợp đồng.
// 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');
});
Client nhận URL cơ sở làm đối số; không có gì trong mã sản phẩm biết rằng nó đang được mock. Trong CI, bạn đặt API_BASE_URL thành địa chỉ của mock trước khi bộ kiểm thử chạy. Mẫu này giữ cho việc mock hoàn toàn tách biệt khỏi logic ứng dụng của bạn, đó là nơi nó thuộc về. Nếu bạn chạy kiểm thử thông qua một pipeline, ý tưởng 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à bước mà hầu hết các nhóm bỏ qua, và nó là bước mang lại hiệu quả lớn. Một máy chủ thật hiếm khi trả về mã 500 khi bạn muốn. Một mock trả về theo yêu cầu.
Cấu hình mock để phục vụ các phản hồi lỗi cụ thể, sau đó xác nhận client của bạn xử lý từng trường hợp:
| Kịch bản | Mock trả về | Điều bạ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 thử lại, sau đó hiển thị một dự phòng |
| Bị giới hạn tốc độ | 429 kèm theo Retry-After |
Client lùi lại đúng mức |
| Phản hồi chậm | 200 sau 5 giây trễ |
Client hết thời gian chờ và phục hồi |
| Phần thân bị lỗi định dạng | 200 với JSON bị hỏng |
Client xử lý lỗi một cách duyên dáng, không bị treo |
Các quy tắc mock nâng cao của Apidog cho phép bạn trả về các phản hồi khác nhau dựa trên yêu cầu, vì vậy một yêu cầu cho order_404 sẽ tạo ra một 404 trong khi mọi ID khác trả về một 200 bình thường. Điều đó mang lại cho bạn một điểm cuối mock bao gồm cả trường hợp thành công và các lỗi. Kết hợp điều này với các xác nhận API mạnh mẽ và bộ kiểm thử của bạn sẽ xác minh hành vi, chứ không chỉ các mã trạng thái.
Tổ chức các mock trong một bộ kiểm thử đang phát triển
Một điểm cuối mock duy nhất thì dễ quản lý. Hàng trăm điểm cuối mock, trải rộng trong một bộ kiểm thử, là lúc các nhóm mất kiểm soát. Một vài thói quen sẽ giúp giữ cho tập hợp này dễ quản lý.
Nhóm các mock theo dịch vụ thực mà chúng đại diện, chứ không phải theo bài kiểm thử sử dụng chúng. Khi API thanh toán thay đổi, bạn muốn có một nơi để cập nhật, chứ không phải hai mươi tệp kiểm thử. Đặt tên các mock fixture theo kịch bản mà chúng đại diện, ví dụ order-shipped hoặc order-rate-limited, để một bài kiểm thử thất bại có thể đọc rõ ràng. Lưu giữ các định nghĩa mock trong hệ thống kiểm soát phiên bản bên cạnh các bài kiểm thử, vì mock là một phần của kiểm thử và xứng đáng được xem xét như nhau.
Hãy chống lại sự thôi thúc cung cấp cho mỗi bài kiểm thử một payload riêng biệt. Hầu hết các bài kiểm thử đều muốn cùng một đối tượng đơn hàng thực tế với một trường được thay đổi. Định nghĩa một phản hồi cơ bản một lần và chỉ ghi đè trường đang được kiểm thử. Điều đó giúp bộ kiểm thử dễ đọc và có nghĩa là một thay đổi hợp đồng chỉ ảnh hưởng đến một định nghĩa cơ bản thay vì các bản sao rải rác. Kỷ luật tương tự giúp bộ kiểm thử tự động hóa API dễ bảo trì cũng áp dụng trực tiếp cho các mock phía sau nó.
Giữ cho mock trung thực
Một mock có thể bị sai lệch. Backend thêm một trường, đổi tên total thành amount, hoặc thay đổi một enum, và mock của bạn vẫn tiếp tục trả về cấu trúc cũ. Các bài kiểm thử vẫn thành công; nhưng môi trường sản phẩm lại gặp lỗi. Đây là cách phổ biến nhất khiến việc mocking gặp trục trặc, và nó diễn ra âm thầm. Không có gì trong bộ kiểm thử của bạn báo lỗi, bởi vì bộ kiểm thử đang đo lường mock dựa trên chính nó.
Hai thói quen sau sẽ giúp ngăn chặn điều này. Thứ nhất, tạo mock từ cùng một lược đồ mà backend công bố, để khi hợp đồng thay đổi, cả hai đều được cập nhật cùng lúc. Một mock được tạo từ tệp OpenAPI sẽ tự động tạo lại khi tệp đó thay đổi; một mock được gõ thủ công thì không. Thứ hai, chạy một bộ kiểm thử hợp đồng nhỏ đối với API thật theo lịch trình. Nhiệm vụ duy nhất của chúng là xác nhận rằng phản hồi trực tiếp vẫn khớp với lược đồ mà các mock của bạn sử dụng. Khi chúng thất bại, bạn sẽ biết mock đã lỗi thời trước khi người dùng phát hiện ra.
Việc xem xét các mock trong quá trình code review cũng rất hữu ích. Khi một pull request thay đổi phản hồi API, người xem xét nên kiểm tra xem mock tương ứng cũng đã được thay đổi chưa. Coi mock như một phần của hợp đồng, thay vì một công cụ hỗ trợ kiểm thử dùng một lần, là điều giúp bộ kiểm thử được mock đáng tin cậy qua nhiều tháng thay đổi.
Nếu bạn muốn một môi trường duy nhất lưu giữ lược đồ, tạo mock và chạy các bài kiểm thử dựa trên đó, hãy Tải xuống Apidog. Nó giữ cho thiết kế, máy chủ mock và bộ kiểm thử đồng bộ, vì vậy mock bạn kiểm thử luôn là hợp đồng hiện tại. Để có thêm nhiều lựa chọn, hãy so sánh trong tổng hợp các công cụ mock API REST này.
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 các bài kiểm thử đơn vị và tích hợp nơi bạn đang kiểm tra mã của chính mình. Giữ một bộ nhỏ các bài kiểm thử hợp đồng và end-to-end truy cập API thật, vì những bài kiểm thử đó xác nhận rằng mock của bạn vẫn khớp với môi trường sản phẩm. Mocking mọi thứ sẽ che giấu sự sai lệch hợp đồng.
Sự khác biệt giữa phản hồi mock tĩnh và động là gì?
Phản hồi tĩnh là một payload JSON cố định không bao giờ thay đổi, tốt cho các xác nhận có thể dự đoán được. Phản hồi động được tạo ra theo từng yêu cầu với các giá trị thực tế, giúp bắt các lỗi mà một payload cố định duy nhất có thể bỏ lỡ. Hầu hết các nhóm đều sử dụng cả hai.
Làm thế nào để đảm bảo mock của tôi luôn chính xác?
Tạo mock từ cùng lược đồ mà backend sử dụng, lý tưởng nhất là tài liệu OpenAPI. Sau đó, chạy các bài kiểm thử hợp đồng theo lịch trình đối với API thật để xác nhận phản hồi trực tiếp vẫn khớp với lược đồ đó. Nếu chúng thất bại, mock của bạn cần được cập nhật.
Một mock có thể mô phỏng các phản hồi chậm hoặc lỗi không?
Có, và đây là một trong những lý do mạnh mẽ nhất để mock. Bạn có thể cấu hình mock để trả về mã 500, mã 429 kèm tiêu đề Retry-After, hoặc một 200 bị trì hoãn. Điều đó cho phép bạn xác minh logic thử lại và các timeout mà một máy chủ thật khỏe mạnh sẽ không bao giờ kích hoạt theo yêu cầu.
Máy chủ mock cục bộ hay máy chủ mock trên đám mây để kiểm thử?
Sử dụng máy chủ mock cục bộ cho các lần chạy kiểm thử. Nó nhanh, không có độ trễ mạng và tránh trạng thái chia sẻ giữa các bản dựng. Sử dụng mock được lưu trữ trên đám mây khi một thiết bị di động, một CI runner hoặc một đối tác bên ngoài cần truy cập mock mà không có quyền truy cập vào máy của bạn.
