Mở đầu — vì sao bài này quan trọng
Nếu bạn đã quen test REST API bằng Postman, bạn sẽ nghĩ chuyển sang GraphQL cũng đơn giản: vẫn POST, vẫn có body, vẫn đọc response JSON. Nhưng đây chính là cái bẫy khiến rất nhiều QA mới tiếp cận GraphQL viết test sai mà không hề hay biết. GraphQL không hoạt động theo tư duy "một endpoint = một chức năng" như REST. Nó chỉ có một endpoint duy nhất — thường là /graphql — và mọi thao tác đọc, ghi, filter đều đi qua đúng cái URL đó. Điều này lật ngược gần như toàn bộ thói quen kiểm thử mà bạn đã xây dựng.
Vì sao bài này đáng để bạn đầu tư nghiêm túc? Bởi vì GraphQL đang phổ biến rất nhanh ở các sản phẩm Việt Nam có frontend phức tạp: các app của Tiki, Momo (một số dịch vụ nội bộ), các startup fintech và các sản phẩm SaaS dùng React/Next.js. Khi công ty bạn ứng tuyển dùng GraphQL mà CV của bạn chỉ ghi "test REST", bạn đã mất điểm. Ngược lại, một QA biết viết test GraphQL đúng cách — biết phân biệt lỗi HTTP với lỗi nghiệp vụ, biết validate cả data lẫn errors, biết dùng variables đúng chuẩn — là người mà đội dev thực sự tin tưởng.
Bài này tập trung riêng vào cách dùng Postman để test GraphQL API: tạo request đúng loại body, viết query/mutation có tham số, và đặc biệt là những assertion mà GraphQL đòi hỏi khác hẳn REST. Tôi sẽ không đi sâu lại vào assertions cơ bản hay scripting sandbox (những bài khác trong khóa đã lo), mà tập trung vào phần "chất GraphQL" của việc kiểm thử.
Khái niệm cốt lõi
GraphQL khác REST như thế nào khi test
Điểm mấu chốt đầu tiên bạn phải khắc cốt: trong GraphQL, HTTP status hầu như luôn là 200 OK — kể cả khi có lỗi. Đây là khác biệt lớn nhất so với REST. Với REST, bạn assert pm.response.to.have.status(404) để biết resource không tồn tại. Với GraphQL, server trả về 200 nhưng nhét thông tin lỗi vào một trường tên là errors trong body. Nếu bạn chỉ kiểm tra status code, test của bạn sẽ luôn pass giả ngay cả khi API hỏng hoàn toàn.
Một response GraphQL điển hình có cấu trúc hai phần:
{
"data": { "user": { "id": "42", "name": "Nguyễn Văn A" } },
"errors": null
}
Khi có sự cố, data có thể là null (hoặc một phần null) và errors là một mảng mô tả vấn đề:
{
"data": null,
"errors": [
{ "message": "User not found", "path": ["user"] }
]
}
Vì vậy quy tắc vàng khi test GraphQL: luôn assert cả data lẫn errors. Một request thành công không chỉ là "có data" mà còn là "không có errors".
Query, Mutation và tại sao body type quan trọng
GraphQL có ba loại thao tác: query (đọc dữ liệu, tương đương GET), mutation (thay đổi dữ liệu, tương đương POST/PUT/DELETE), và subscription (nhận dữ liệu realtime, ít test bằng Postman thuần). Dù là loại nào, bạn vẫn gửi bằng HTTP POST tới cùng một URL.
Trong Postman, có hai cách gửi query:
Cách 1 — dùng tab Body "GraphQL" (khuyên dùng): Postman có sẵn một loại body tên là GraphQL, tách riêng phần QUERY và phần GraphQL Variables thành hai ô. Postman sẽ tự lo việc đóng gói thành JSON đúng chuẩn, và còn cung cấp autocomplete nếu bạn nhập được schema. Đây là cách sạch sẽ, dễ đọc nhất.
Cách 2 — dùng raw JSON: Bạn tự đóng gói query thành một object JSON với hai key: query và variables. Cách này hữu ích khi bạn cần kiểm soát chính xác payload hoặc khi đưa vào Newman/data-driven test.
{
"query": "query GetUser($id: ID!) { user(id: $id) { name email } }",
"variables": { "id": "42" }
}
Variables — đừng nối chuỗi
Người mới rất hay mắc lỗi nối trực tiếp giá trị vào query: user(id: "42"). Cách làm chuẩn là khai báo biến trong query ($id: ID!) và truyền giá trị qua phần variables. Lợi ích: query trở thành template tái sử dụng được, tránh lỗi escape khi giá trị có dấu tiếng Việt hay ký tự đặc biệt, và dễ dàng data-driven hóa sau này. Dấu ! trong ID! nghĩa là biến bắt buộc — nếu thiếu, server báo lỗi ngay.
Tình huống thực tế
Ví dụ 1 — Fintech Cần Thơ và cái bẫy status 200
Một team QA tại một startup ví điện tử ở Cần Thơ (khoảng 15 người) chuyển API tài khoản sang GraphQL. Bạn Trang, QA mới, viết bộ test kiểm tra "lấy thông tin ví theo ID". Cô assert đúng kiểu REST: pm.response.to.have.status(200) và regression suite báo 100% pass suốt hai sprint. Đến khi khách hàng phàn nàn app hiển thị "số dư: null", team mới phát hiện: backend đã đổi tên trường balance thành walletBalance, khiến field cũ trả về null kèm một entry trong errors — nhưng HTTP vẫn là 200, nên test của Trang không hề bắt được.
Bài học: GraphQL status 200 gần như vô nghĩa để đánh giá đúng/sai. Sau sự cố, team thêm một assertion bắt buộc vào mọi request:
pm.test("Không có GraphQL errors", () => {
const body = pm.response.json();
pm.expect(body.errors, JSON.stringify(body.errors)).to.be.undefined;
});
Từ đó bộ test bắt được ngay khi field bị đổi tên, vì response trả errors thay vì im lặng.
Ví dụ 2 — Sàn TMĐT và query "over-fetching" gây chậm
Một sàn thương mại điện tử giả định tên ShopViet dùng GraphQL cho trang sản phẩm. Bạn Hùng viết test lấy sản phẩm và, vì tiện, copy nguyên query mà frontend dùng — bao gồm cả reviews, relatedProducts, sellerRating... hàng chục field lồng nhau. Test chạy đúng nhưng mỗi lần mất gần 3 giây. Khi đưa 200 test này vào Newman chạy trong CI, pipeline phình từ 2 phút lên 12 phút.
Team review lại và nhận ra: mục đích test chỉ là kiểm tra id, name, price có đúng không — không cần kéo cả cây dữ liệu. Đây chính là sức mạnh của GraphQL mà REST không có: bạn tự quyết định lấy đúng field mình cần. Hùng rút query xuống còn ba field, thời gian mỗi request giảm còn ~400ms, pipeline về lại ~3 phút.
Bài học: Trong test GraphQL, hãy chỉ query đúng field cần assert. Query gọn không chỉ chạy nhanh mà còn khiến test ít vỡ hơn khi các field không liên quan thay đổi.
Ví dụ 3 — Mutation tạo đơn hàng và chuyện validate nghiệp vụ
Một team tại Đà Nẵng test mutation createOrder. Ban đầu họ chỉ assert data.createOrder.id khác null. Nhưng QA lead nhận ra: một đơn hàng "được tạo" chưa chắc đúng — cần kiểm tra trạng thái, tổng tiền, và cả trường hợp lỗi có kiểm soát (ví dụ hết hàng phải trả errors với message rõ ràng, không phải crash 500). Họ tách thành hai kịch bản: happy path (assert data), và negative path (đặt số lượng vượt tồn kho, assert errors[0].message chứa "out of stock" và data.createOrder là null).
Bài học: Với mutation, test negative case quan trọng ngang test happy path. GraphQL cho phép trả lỗi nghiệp vụ có cấu trúc — hãy tận dụng để assert đúng thông điệp lỗi.
Hướng dẫn từng bước
Bước 1 — Tạo request POST tới endpoint. Tạo request mới, chọn method POST, URL đặt là {{base_url}}/graphql (dùng biến môi trường để chuyển dev/staging/prod dễ dàng).
Bước 2 — Chọn body type GraphQL. Vào tab Body, chọn radio GraphQL (không phải raw). Bạn sẽ thấy hai ô: QUERY và GraphQL Variables.
Bước 3 — Viết query có biến. Trong ô QUERY:
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
Trong ô GraphQL Variables:
{ "id": "42" }
Bước 4 — Thêm header xác thực nếu cần. Nhiều GraphQL API cần token: thêm header Authorization: Bearer {{token}}. (Chi tiết về OAuth/JWT đã có ở bài Authorization của khóa.)
Bước 5 — Gửi và đọc response. Nhấn Send. Kiểm tra body có data.user với các field mong đợi.
Bước 6 — Viết Tests script. Chuyển sang tab Tests và viết assertion bao trùm cả hai mặt:
const body = pm.response.json();pm.test("HTTP 200", () => {
pm.response.to.have.status(200);
});
pm.test("Không có errors", () => {
pm.expect(body.errors, JSON.stringify(body.errors)).to.be.undefined;
});
pm.test("Trả về đúng user", () => {
pm.expect(body.data.user).to.be.an("object");
pm.expect(body.data.user.id).to.eql("42");
pm.expect(body.data.user.email).to.include("@");
});
Bước 7 — Thêm một request negative. Đổi id sang giá trị không tồn tại, và assert rằng errors xuất hiện, data.user là null:
const body = pm.response.json();
pm.test("Trả lỗi khi user không tồn tại", () => {
pm.expect(body.data.user).to.be.null;
pm.expect(body.errors).to.be.an("array").that.is.not.empty;
});
Bước 8 — Test một mutation. Tạo request mới với query dạng mutation:
mutation CreateOrder($productId: ID!, $qty: Int!) {
createOrder(productId: $productId, quantity: $qty) {
id
status
total
}
}
Variables: { "productId": "1001", "qty": 2 }. Assert data.createOrder.status bằng "PENDING" và total là số dương.
Lỗi thường gặp & mẹo
Chỉ assert status code. Đây là lỗi số một. Nhắc lại lần nữa: status 200 không đảm bảo request thành công trong GraphQL. Luôn kiểm tra errors.
Nhầm ô QUERY và ô Variables. Nếu bạn dán cả object { "query": ..., "variables": ... } vào ô QUERY của body type GraphQL, Postman sẽ báo lỗi cú pháp. Ô QUERY chỉ chứa phần query thuần, variables đặt riêng ô kia. Ngược lại, nếu dùng raw JSON thì phải gói cả hai vào một object.
Quên dấu ! khi biến bắt buộc. Khai báo $id: ID (không có !) khiến server cho phép truyền null và có thể trả kết quả khó hiểu. Nếu field bắt buộc, hãy dùng ID!.
Copy nguyên query của frontend. Như ví dụ ShopViet, query quá lớn làm test chậm và dễ vỡ. Chỉ lấy field bạn thực sự assert.
Không xử lý được partial data. GraphQL có thể trả về data một phần kèm errors cho phần lỗi. Nếu test của bạn giả định "có errors thì data phải null", bạn sẽ sai trong trường hợp partial. Hãy assert đúng theo hành vi thực tế của API.
Mẹo — lưu giá trị từ response để chain. Sau khi tạo order, lưu id: pm.collectionVariables.set("orderId", body.data.createOrder.id); để request sau dùng lại. (Kỹ thuật chained request có bài riêng trong khóa.)
Mẹo — bật GraphQL schema để có autocomplete. Nếu API cung cấp introspection, Postman có thể tải schema và gợi ý field khi bạn gõ query, giúp giảm lỗi đánh máy tên field.
Mẹo — dùng biến môi trường cho endpoint. Đặt {{base_url}} thay vì hardcode URL, để cùng một collection chạy được trên dev/staging/prod.
Bài tập thực hành
Dùng một GraphQL API công khai để luyện tập, ví dụ https://countries.trevorblades.com/ (API danh sách quốc gia, không cần token).
- Query cơ bản: Tạo request POST với body type GraphQL, viết query lấy tên và mã điện thoại của Việt Nam:
query GetCountry($code: ID!) {
country(code: $code) { name phone capital }
}
Variables: { "code": "VN" }. Viết Tests assert data.country.name bằng "Vietnam", không có errors, và phone bằng "84".- Chỉ lấy field cần thiết: Viết lại query trên nhưng chỉ lấy
name. Quan sát response gọn hơn và tự rút ra kết luận về over-fetching.
- Negative case: Đổi
codethành"ZZ"(mã không tồn tại). Viết assertion phù hợp với hành vi API trả về (kiểm tradata.countrylà null hay cóerrors) — hãy quan sát response thật rồi mới viết assert.
- Nâng cao: Viết một query lấy danh sách tất cả ngôn ngữ của một continent, rồi assert mảng trả về có độ dài lớn hơn 0 và mỗi phần tử có field
name.
Tóm tắt
Test GraphQL bằng Postman khác REST ở ba điểm cốt lõi mà bạn phải nhớ. Thứ nhất, mọi thao tác đều là POST tới một endpoint duy nhất (/graphql), với body type GraphQL tách riêng QUERY và Variables — hoặc raw JSON gói query + variables. Thứ hai, status 200 không có nghĩa là thành công: luôn assert cả data lẫn errors, vì GraphQL nhét lỗi vào body chứ không vào HTTP status. Thứ ba, hãy tận dụng khả năng chọn field của GraphQL — chỉ query đúng thứ bạn cần assert để test nhanh và bền hơn.
Dùng variables thay vì nối chuỗi, khai báo ! cho biến bắt buộc, và luôn có ít nhất một negative case bên cạnh happy path — đặc biệt với mutation. Nắm chắc những nguyên tắc này, bạn sẽ viết được bộ test GraphQL đáng tin cậy, không rơi vào bẫy "pass giả" mà rất nhiều QA vẫn mắc phải khi mới chuyển từ REST sang.