Product Management
Đăng nhập
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Mock với Prism (OpenAPI-driven)

Mở đầu — vì sao bài này quan trọng

Trong suốt khóa học, chúng ta đã dùng Postman Mock Server để giả lập API. Nhưng nếu bạn đã từng làm việc trong một team có backend chậm hơn frontend, bạn sẽ nhận ra một vấn đề nhức nhối: Postman Mock sinh response từ example mà bạn tự tay lưu vào request. Nghĩa là mock chỉ "đúng" khi bạn nhớ cập nhật example mỗi lần API đổi. Còn nếu example lệch với hợp đồng thật (contract) thì mock nói dối, và frontend build lên một giao diện chạy trên dữ liệu ảo không bao giờ tồn tại thật.

Prism của Stoplight tiếp cận theo hướng ngược lại và mạnh mẽ hơn cho nhiều tình huống: nó sinh mock trực tiếp từ OpenAPI spec. Không cần bạn lưu từng example thủ công — chỉ cần file openapi.yaml là Prism dựng ngay một server giả có đầy đủ endpoint, kiểu dữ liệu, validation request, và thậm chí trả về lỗi 422 nếu bạn gửi payload sai schema. Đây là "OpenAPI-driven mocking" — mock được điều khiển bởi hợp đồng, chứ không phải bởi ví dụ.

Bài này quan trọng vì nó mở rộng tư duy của bạn ra ngoài hệ sinh thái Postman thuần túy. Là một API tester giỏi, bạn cần biết công cụ nào mạnh ở đâu: khi nào dùng Postman Mock (nhanh, trực quan, chia sẻ trong team qua workspace), khi nào cần Prism (kỷ luật theo spec, chạy tốt trong CI, phục vụ phát triển song song frontend–backend). Sau bài này bạn sẽ cài đặt được Prism, kết hợp nó với Postman để test, và hiểu triết lý "spec là nguồn chân lý".

Khái niệm cốt lõi

Prism là gì

Prism là một công cụ dòng lệnh (CLI) mã nguồn mở do Stoplight phát triển. Nó có hai chế độ chính:

  • prism mock — đọc file OpenAPI và dựng một HTTP server giả lập toàn bộ API mô tả trong spec. Đây là phần chúng ta quan tâm nhất trong bài này.
  • prism proxy — đứng giữa client và API thật, kiểm tra xem request/response có tuân thủ spec hay không (validation proxy). Hữu ích cho contract testing, nhưng đó là phần liên quan Bài 29.
Điểm khác biệt cốt lõi so với Postman Mock: Postman đọc example đã lưu, Prism đọc schema. Nếu spec khai báo trường priceinteger với minimum: 0, Prism sẽ tự sinh ra một số nguyên hợp lệ ngay cả khi bạn chưa viết example nào.

Static vs Dynamic examples

Prism có hai cách sinh dữ liệu response:

  • Static (mặc định): nếu trong spec có example hoặc examples, Prism ưu tiên trả về đúng giá trị đó. Ổn định, dễ đoán — phù hợp khi frontend cần một dữ liệu cố định để dựng UI.
  • Dynamic (--dynamic): Prism bỏ qua example và sinh dữ liệu ngẫu nhiên nhưng hợp lệ theo schema mỗi lần gọi. Tên sẽ là chuỗi ngẫu nhiên, số nằm trong khoảng min–max, enum sẽ chọn một giá trị hợp lệ. Rất tốt để test frontend có xử lý được đủ loại dữ liệu hay không, thay vì chỉ chạy trên một mẫu đẹp.

Content negotiation và response mẫu

Prism thông minh trong việc chọn response nào để trả. Bạn có thể điều khiển bằng header:

  • Prefer: code=404 — buộc Prism trả về response 404 mô tả trong spec, để bạn test luồng lỗi.
  • Prefer: example=notFound — chọn đúng một example có tên trong spec.
  • Accept: application/json — chọn media type.
Đây là thứ Postman Mock cũng làm được (qua header x-mock-response-code), nhưng Prism làm theo chuẩn OpenAPI nên nhất quán với mọi công cụ khác trong hệ sinh thái spec-first.

Request validation — điểm ăn tiền

Đây là lý do nhiều team chọn Prism. Khi bạn gửi một request sai schema (thiếu trường bắt buộc, sai kiểu dữ liệu, vi phạm minLength...), Prism tự động trả về 422 Unprocessable Entity kèm mô tả lỗi chi tiết. Postman Mock mặc định không làm việc này — nó cứ trả example bất kể bạn gửi gì. Với Prism, mock của bạn hành xử gần giống backend thật đang validate đầu vào, giúp frontend phát hiện bug ngay từ khi backend còn chưa viết dòng code nào.

Tình huống thực tế

Ví dụ 1 — Tiki: frontend không phải chờ backend

Giả sử một team tại Tiki đang xây tính năng "Ví Astra" mới. Backend cần 3 tuần để hoàn thiện API /wallet/balance/wallet/transactions, nhưng team frontend muốn bắt đầu ngay hôm nay. Trưởng nhóm API viết trước file wallet-openapi.yaml mô tả đầy đủ endpoint, schema Transaction với các trường id, amount (integer, VND), type (enum: credit/debit), created_at.

Frontend chạy prism mock wallet-openapi.yaml --dynamic trên máy mình. Ngay lập tức họ có một server tại http://127.0.0.1:4010 trả về danh sách giao dịch ngẫu nhiên nhưng đúng cấu trúc. Vì bật --dynamic, mỗi lần gọi lại có amount khác nhau — có giao dịch 5.000 VND, có giao dịch 12.000.000 VND — nhờ đó frontend phát hiện sớm rằng UI của họ vỡ layout khi số tiền quá lớn. Bài học: mock động theo schema giúp lộ bug edge case mà một example đẹp cố định (kiểu Postman example) sẽ che giấu.

Ví dụ 2 — Một fintech ở TP.HCM: mock biết từ chối request sai

Một công ty fintech đang tích hợp cổng thanh toán. Đội QA muốn test tầng gọi API của app trước khi sandbox thật của đối tác sẵn sàng. Họ có spec mô tả endpoint POST /payments yêu cầu amount (bắt buộc, integer, minimum: 1000) và order_id (bắt buộc, string).

Họ chạy prism mock payments.yaml. Khi tester gửi một request thiếu order_id, Prism trả về 422 với thông báo "should have required property 'order_id'". Khi gửi amount: 500, Prism báo vi phạm minimum. Nhờ vậy đội dev sửa được logic validate phía client trước khi cắm vào sandbox thật, tránh tiêu tốn quota gọi sandbox và tránh những bug ngớ ngẩn lọt tới tận môi trường tích hợp. Bài học: Prism không chỉ giả lập "đường hạnh phúc" mà còn ép request phải hợp lệ, biến mock thành một lớp kiểm thử contract nhẹ ngay tại chỗ.

Ví dụ 3 — Startup SaaS: một spec, mock chạy trong CI cho cả team

Một startup SaaS quản lý kho hàng có 4 microservice. Họ đặt tất cả spec trong repo api-specs. Trong pipeline CI, mỗi khi ai đó sửa spec, một job Docker chạy stoplight/prism mock lên rồi Newman chạy collection Postman test ngược lại mock đó. Nếu spec bị viết sai (ví dụ đổi status từ enum sang string tự do), test Postman sẽ đỏ ngay. Đồng thời team QA dùng cùng một mock để chạy các collection kiểm thử mà không cần dựng cả 4 service thật trên máy — vốn tốn RAM và cấu hình database. Bài học: vì Prism không giữ state và chạy từ một file duy nhất, nó rất hợp để làm "server dùng một lần" trong CI, giúp test lặp lại được (reproducible) và độc lập với hạ tầng.

Hướng dẫn từng bước

Bước 1 — Cài đặt Prism

Prism là gói npm, cần Node.js. Cài toàn cục để dùng như một lệnh:

npm install -g @stoplight/prism-cli

Kiểm tra

prism --version

Nếu không muốn cài toàn cục, có thể chạy trực tiếp qua npx:

npx @stoplight/prism-cli mock openapi.yaml

Bước 2 — Chuẩn bị một file OpenAPI tối giản

Tạo file store.yaml để thực hành:

openapi: 3.0.3
info:
  title: Store API
  version: 1.0.0
paths:
  /products/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                required: [id, name, price]
                properties:
                  id: { type: integer, example: 1 }
                  name: { type: string, example: "Áo thun cổ tròn" }
                  price: { type: integer, minimum: 0, example: 149000 }
        '404':
          description: Không tìm thấy
  /products:
    post:
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, price]
              properties:
                name: { type: string, minLength: 1 }
                price: { type: integer, minimum: 0 }
      responses:
        '201': { description: Đã tạo }

Bước 3 — Chạy mock server

prism mock store.yaml

Mặc định Prism lắng nghe ở http://127.0.0.1:4010. Terminal sẽ liệt kê các route nó dựng lên.

Bước 4 — Gọi thử bằng Postman (hoặc curl)

Mở Postman, tạo request GET http://127.0.0.1:4010/products/1. Bạn sẽ nhận về JSON đúng theo example đã khai trong spec. Trong tab Tests của Postman, viết assertion như bình thường:

pm.test("Status 200", () => pm.response.to.have.status(200));
pm.test("Có trường price là số", () => {
  const body = pm.response.json();
  pm.expect(body.price).to.be.a('number');
});

Điểm hay: bạn viết test Postman đúng như test API thật, chỉ khác base URL trỏ về Prism. Sau này khi backend thật xong, chỉ cần đổi environment variable baseUrl là chạy lại được toàn bộ collection.

Bước 5 — Ép Prism trả về lỗi để test luồng negative

Thêm header Prefer vào request Postman:

Prefer: code=404

Prism sẽ trả về response 404 mô tả trong spec, giúp bạn test cách frontend/collection xử lý khi sản phẩm không tồn tại.

Bước 6 — Thử request validation

Gửi POST http://127.0.0.1:4010/products với body { "price": -5 } (thiếu name, price âm). Prism trả về 422 kèm chi tiết lỗi. Đây là hành vi bạn không có với Postman Mock.

Bước 7 — Bật dynamic mode

prism mock store.yaml --dynamic

Giờ mỗi lần gọi GET /products/1 sẽ ra dữ liệu ngẫu nhiên hợp schema. Dùng chế độ này khi muốn stress-test cách UI hoặc collection xử lý dữ liệu đa dạng.

Bước 8 — Đưa vào CI với Docker (không cần cài Node)

docker run --init --rm -p 4010:4010 \
  -v $(pwd):/tmp stoplight/prism:4 \
  mock -h 0.0.0.0 /tmp/store.yaml

Lưu ý -h 0.0.0.0 để Prism lắng nghe trên mọi interface bên trong container, nếu không bạn sẽ không truy cập được từ ngoài. Sau đó cho Newman chạy collection trỏ vào http://localhost:4010.

Lỗi thường gặp & mẹo

Chỉ nghe trên 127.0.0.1 nên container/CI không gọi được. Đây là lỗi kinh điển khi chạy Prism trong Docker hoặc GitHub Actions. Mặc định Prism bind vào 127.0.0.1, tức chỉ chính container đó truy cập được. Luôn thêm -h 0.0.0.0 khi chạy trong container.

Quên rằng static mode luôn trả cùng một example. Nhiều bạn thắc mắc "sao mock lúc nào cũng trả id: 1?". Vì ở chế độ static Prism ưu tiên example trong spec. Muốn dữ liệu đa dạng thì bật --dynamic, hoặc khai nhiều examples có tên rồi chọn bằng Prefer: example=<tên>.

Kỳ vọng Prism có state như database thật. Prism là stateless. Nếu bạn POST tạo một sản phẩm rồi GET lại, nó sẽ không "nhớ" — vì nó chỉ trả theo spec, không lưu gì. Đừng viết test kiểu tạo-rồi-đọc-lại trên Prism; loại kịch bản chained request có state hãy để dành cho API thật hoặc Postman Mock với logic phù hợp.

Spec sai khiến mock im lặng trả sai. Prism trung thành tuyệt đối với spec. Nếu bạn khai nhầm kiểu dữ liệu, mock sẽ nhân rộng cái sai đó. Mẹo: validate spec trước bằng spectral lint (cũng của Stoplight) hoặc prism sẽ báo lỗi khi nạp spec hỏng.

Nhầm giữa mockproxy. prism mock giả lập API từ spec; prism proxy chuyển tiếp tới API thật và kiểm tra tuân thủ. Đừng dùng nhầm — trong bài này ta luôn dùng mock.

Mẹo phối hợp với Postman: đặt baseUrl trong environment. Có một environment "Prism-local" trỏ về http://127.0.0.1:4010 và một environment "Prod" trỏ về API thật. Cùng một collection, chỉ đổi environment là chuyển giữa mock và thật — đúng tinh thần Bài 5 về environment setup.

Mẹo --errors: thêm cờ --errors để Prism trả lỗi validation nghiêm ngặt hơn (ví dụ 422 cả khi request thiếu security scheme), giúp mock hành xử "cứng" như backend production.

Bài tập thực hành

  • Dựng mock đầu tiên: Cài Prism, dùng file store.yaml ở trên chạy prism mock. Gọi GET /products/1 bằng Postman và viết 2 assertion kiểm tra status và kiểu của trường price.
  • Test luồng lỗi: Thêm header Prefer: code=404 và xác nhận Prism trả 404. Viết một test Postman khẳng định response là 404 để mô phỏng luồng "sản phẩm không tồn tại".
  • Kiểm chứng request validation: Gửi POST /products với body thiếu trường name. Ghi lại nội dung lỗi 422 Prism trả về. So sánh: nếu làm điều tương tự trên Postman Mock, bạn nhận được gì?
  • Static vs dynamic: Chạy lần lượt prism mock store.yamlprism mock store.yaml --dynamic. Gọi GET /products/1 mỗi chế độ 3 lần. Ghi lại sự khác biệt trong dữ liệu trả về và giải thích khi nào nên dùng chế độ nào.
  • Đưa vào Newman: Chạy Prism qua Docker với -h 0.0.0.0 -p 4010:4010, xuất collection Postman của bạn ra file, rồi dùng Newman chạy collection đó trỏ về mock. Mục tiêu: mô phỏng một job CI test collection ngược lại spec mà không cần backend thật.

Tóm tắt

Prism là công cụ mock điều khiển bởi OpenAPI spec — khác hẳn Postman Mock vốn dựa trên example thủ công. Vì mock sinh trực tiếp từ schema, nó luôn khớp với hợp đồng API, hỗ trợ phát triển song song frontend–backend, tự động validate request và trả về 422 khi payload sai, và chạy gọn trong CI/Docker như một server dùng một lần.

Những điểm cần khắc cốt ghi tâm: static mode trả example cố định, --dynamic sinh dữ liệu ngẫu nhiên hợp schema; dùng header Prefer để chọn mã lỗi hoặc example; nhớ -h 0.0.0.0 khi chạy trong container; và Prism là stateless nên đừng kỳ vọng nó nhớ dữ liệu bạn vừa tạo. Về mặt phối hợp, hãy giữ nguyên collection Postman và chỉ đổi baseUrl qua environment để chuyển giữa Prism và API thật.

Tư duy quan trọng nhất bạn mang đi: spec là nguồn chân lý. Khi API được mô tả tử tế bằng OpenAPI, bạn không chỉ có tài liệu — bạn có luôn một server giả, một bộ validate, và một điểm neo cho test. Postman và Prism không loại trừ nhau; một tester thành thạo biết dùng Postman cho sự trực quan và cộng tác, dùng Prism khi cần kỷ luật hợp đồng và tự động hóa. Đó chính là dấu hiệu của người đang tiến từ "người dùng Postman" thành một API tester thực thụ.

Học xong bài này rồi? Tạo tài khoản miễn phí để lưu lại — lần sau vào là biết ngay đang dở ở đâu, và học hết khóa thì có chứng chỉ. Lưu tiến độ của tôi