Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa chạy xong một collection kiểm thử API bán hàng. Kết quả trả về là một cục JSON dài 400 dòng, chứa 50 đơn hàng, mỗi đơn có trạng thái, tổng tiền, danh sách sản phẩm. Sếp của bạn — một chị trưởng nhóm QA không đọc code — ghé qua và hỏi: "Hôm nay có bao nhiêu đơn bị lỗi thanh toán?". Nếu bạn ngồi cuộn chuột qua JSON để đếm bằng mắt, bạn đã thua. Còn nếu ngay dưới tab response hiện lên một bảng gọn gàng, hoặc một biểu đồ cột đỏ-xanh, bạn chỉ cần chỉ tay: "Đây, 3 đơn đỏ này là lỗi". Đó chính là sức mạnh của Postman Visualizer.
Visualizer là một tính năng bị đánh giá thấp một cách oan uổng. Nhiều người dùng Postman nhiều năm mà chưa từng mở tab Visualize. Nhưng với người làm QA nghiêm túc, đây là công cụ biến dữ liệu thô thành thứ mà con người đọc được trong 3 giây — không cần export ra Excel, không cần dán vào công cụ ngoài, không cần rời khỏi Postman. Trong bài này chúng ta sẽ đi sâu vào cách render response thành bảng, biểu đồ và dashboard HTML tùy biến ngay bên trong Postman. Đây là kỹ năng làm cho báo cáo kiểm thử của bạn "biết nói".
Lưu ý phạm vi: bài này chỉ tập trung vào Visualizer — công cụ render HTML tùy biến ngay trong tab response. Nó khác với Newman Reporters (báo cáo HTML sau khi chạy CLI, đã có bài riêng) và khác với báo cáo tài liệu test tổng thể. Ở đây ta nói về việc "vẽ" ngay trong một request đơn lẻ.
Khái niệm cốt lõi
Visualizer là gì
Visualizer cho phép bạn viết một đoạn HTML template (kèm CSS và JavaScript nếu muốn), bơm dữ liệu vào đó, và Postman sẽ render nó thành một trang web nhỏ ngay trong tab Visualize của response. Cơ chế nằm gọn trong một hàm duy nhất:
pm.visualizer.set(template, data);
template: một chuỗi HTML. Bên trong dùng cú pháp Handlebars ({{...}}) để chèn dữ liệu động.data: một object JavaScript. Các thuộc tính của nó sẽ được truyền vào template để Handlebars điền vào chỗ{{...}}.
Một bảng cơ bản
Ví dụ kinh điển nhất là biến một mảng JSON thành bảng HTML:
const response = pm.response.json();const template =
<style>
table { width: 100%; border-collapse: collapse; font-family: sans-serif; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background: #16a34a; color: white; }
tr:nth-child(even) { background: #f6f8fa; }
</style>
<table>
<tr><th>Mã đơn</th><th>Khách hàng</th><th>Tổng tiền</th><th>Trạng thái</th></tr>
{{#each orders}}
<tr>
<td>{{ma_don}}</td>
<td>{{khach_hang}}</td>
<td>{{tong_tien}}</td>
<td>{{trang_thai}}</td>
</tr>
{{/each}}
</table>
;
pm.visualizer.set(template, { orders: response.data });
Ba điểm cần nhớ ngay:
{{#each ...}} ... {{/each}}là cú pháp lặp của Handlebars — dùng để duyệt qua mảng.- Bên trong template, dữ liệu được tham chiếu qua tên thuộc tính bạn đặt trong object
data. Ở đây ta đóng gói mảng vào keyorders, nên template dùng{{#each orders}}. - CSS phải nhúng thẳng vào template bằng thẻ
<style>, vì Visualizer render trong một sandbox iframe cô lập — không kế thừa style của Postman.
Handlebars — vài cú pháp thực dụng
Visualizer dùng Handlebars làm engine template. Bạn không cần học sâu, chỉ cần vài mảnh hay dùng:
{{ten}}— in giá trị (tự động escape HTML, an toàn).{{{html_tho}}}— in HTML thô không escape (dùng cẩn thận).{{#each ds}} ... {{/each}}— lặp qua mảng; trong đó{{this}}là phần tử hiện tại nếu là mảng giá trị đơn.{{#if dieu_kien}} ... {{else}} ... {{/if}}— điều kiện.{{@index}}— số thứ tự trong vòng lặp (bắt đầu từ 0).
{{#each orders}}
<tr class="{{#if loi}}row-error{{/if}}">
<td>{{ma_don}}</td>
<td>{{trang_thai}}</td>
</tr>
{{/each}}
Vẽ biểu đồ với thư viện ngoài
Vì template là HTML đầy đủ, bạn có thể nhúng thư viện JavaScript qua CDN — phổ biến nhất là Chart.js. Postman cho phép tải script ngoài trong sandbox Visualizer. Mô hình chung: đặt một thẻ <canvas>, nhúng Chart.js, rồi lấy dữ liệu qua pm.getData().
const response = pm.response.json();const template =
<canvas id="bieudo" height="120"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
pm.getData(function (err, data) {
new Chart(document.getElementById('bieudo'), {
type: 'bar',
data: {
labels: data.labels,
datasets: [{
label: 'Số đơn theo trạng thái',
data: data.values,
backgroundColor: ['#16a34a', '#f59e0b', '#dc2626']
}]
}
});
});
</script>
;
pm.visualizer.set(template, {
labels: ['Thành công', 'Chờ xử lý', 'Thất bại'],
values: [42, 5, 3]
});
Điểm mấu chốt: bên trong <script> của template, bạn không truy cập trực tiếp biến data của Postman mà phải gọi pm.getData(callback) để lấy dữ liệu đã set. Đây là điểm khiến rất nhiều người mới bối rối — họ cố dùng {{values}} trong JavaScript và nhận về chuỗi rỗng. Handlebars {{...}} chỉ hoạt động ở phần HTML tĩnh; còn khi cần dữ liệu trong JS động (như vẽ chart), phải dùng pm.getData().
Tình huống thực tế
Ví dụ 1 — Tiki: bảng tồn kho đỏ cho buổi review nhanh
Một bạn QA tại đội kiểm thử của sàn thương mại điện tử (gọi là "TikiShop" cho ví dụ) phụ trách API /inventory/warehouse. Endpoint trả về danh sách 120 SKU với số lượng tồn. Mỗi sáng team có buổi standup 15 phút, và Product Owner luôn hỏi: "Những SKU nào sắp hết hàng (dưới 10)?".
Trước đây bạn ấy phải copy JSON, dán vào Google Sheets, lọc, rồi chụp màn hình. Mất 6-7 phút mỗi sáng. Sau khi học Visualizer, bạn ấy viết một template render bảng, và thêm logic tô đỏ những dòng tồn dưới 10:
const items = pm.response.json().items.map(x => ({
...x, canhbao: x.ton_kho < 10
}));
const template =
<style>
.row-warn { background:#fde8e8; color:#b91c1c; font-weight:600; }
td,th { padding:6px 10px; border-bottom:1px solid #eee; }
</style>
<table>
<tr><th>SKU</th><th>Tên</th><th>Tồn</th></tr>
{{#each items}}
<tr class="{{#if canhbao}}row-warn{{/if}}">
<td>{{sku}}</td><td>{{ten}}</td><td>{{ton_kho}}</td>
</tr>
{{/each}}
</table>;
pm.visualizer.set(template, { items });
Bài học rút ra: phần "logic" (tính canhbao) nên làm trong JavaScript của tab Tests, không nhét vào template. Template chỉ nên lo phần hiển thị. Nhờ tách bạch vậy, mỗi sáng bạn ấy chỉ cần bấm Send, mở tab Visualize, và đọc ngay — thời gian chuẩn bị từ 7 phút xuống còn 10 giây.
Ví dụ 2 — Ngân hàng số: dashboard tỷ lệ pass/fail của bộ smoke test
Một team QA ở một ngân hàng số tại TP.HCM chạy một request tổng hợp gọi tới endpoint nội bộ /qa/smoke-summary, trả về số test pass/fail của 8 nhóm nghiệp vụ (đăng nhập, chuyển khoản, nạp tiền, thanh toán hóa đơn...). Trưởng nhóm muốn một cái nhìn "một màn hình" để dán vào biên bản họp release.
Họ dùng Visualizer với Chart.js để vẽ biểu đồ cột chồng (stacked bar) pass/fail, kèm một dòng tóm tắt tỷ lệ pass tổng. Con số cụ thể của một lần chạy: 236 test, 228 pass, 8 fail — tỷ lệ 96,6%. Biểu đồ hiển thị rõ nhóm "thanh toán hóa đơn" chiếm 6/8 lỗi, giúp cả phòng lập tức biết cần soi vào đâu.
Bài học rút ra: Visualizer rất mạnh khi bạn có một endpoint "tổng hợp sẵn số liệu". Thay vì bắt Postman tự tính toán phức tạp trên hàng nghìn bản ghi (dễ chậm), hãy để backend hoặc một request tính toán trước, rồi Visualizer chỉ lo phần "vẽ". Ngoài ra, một ảnh chụp tab Visualize dán vào biên bản họp trông chuyên nghiệp hơn hẳn một khối JSON.
Ví dụ 3 — Startup fintech: cạm bẫy Unicode tiếng Việt
Một startup ví điện tử render bảng lịch sử giao dịch có tên khách hàng tiếng Việt ("Nguyễn Thị Hồng Nhung"). Ban đầu bảng hiển thị tốt, nhưng khi khách có ký tự đặc biệt trong ghi chú (ví dụ dấu < trong "giảm giá <50k>"), bảng bị vỡ layout. Nguyên nhân: họ dùng {{{ghi_chu}}} (ba ngoặc, in HTML thô) thay vì {{ghi_chu}} (hai ngoặc, tự escape).
Sau khi đổi về hai ngoặc, Handlebars tự động escape ký tự <, >, & thành thực thể HTML an toàn, bảng hiển thị đúng và tránh luôn nguy cơ XSS từ dữ liệu người dùng.
Bài học rút ra: mặc định luôn dùng {{...}} (hai ngoặc) để dữ liệu được escape. Chỉ dùng ba ngoặc {{{...}}} khi bạn CHỦ ĐỘNG muốn render HTML và chắc chắn nguồn dữ liệu đáng tin cậy.
Hướng dẫn từng bước
Hãy dựng một Visualizer hoàn chỉnh cho một request thực tế.
Bước 1 — Chọn request và chạy thử. Gửi request tới API của bạn, xác nhận response trả về JSON đúng cấu trúc. Nhìn kỹ hình dạng dữ liệu: mảng nằm ở đâu, tên các trường là gì. Đây là bước quan trọng nhất — 80% lỗi Visualizer đến từ việc gọi sai đường dẫn tới mảng.
Bước 2 — Mở tab Scripts → Post-response (hoặc Tests). Lấy dữ liệu:
const body = pm.response.json();
Bước 3 — Chuẩn hóa dữ liệu trong JavaScript. Nếu cần lọc, tính toán, thêm cờ (như canhbao, loi), làm hết ở đây. Đừng để template phải tính. Kết quả là một object sạch sẽ, ví dụ { items: [...], tong: 42 }.
Bước 4 — Viết template HTML. Bắt đầu với <style> cho đẹp, rồi phần HTML dùng Handlebars. Với bảng thì dùng {{#each}}. Giữ template trong một biến chuỗi (dùng template literal với dấu backtick để xuống dòng thoải mái).
Bước 5 — Gọi pm.visualizer.set(template, data). Truyền template và object dữ liệu đã chuẩn hóa.
Bước 6 — Bấm Send lại và mở tab Visualize. Tab này nằm cạnh Body, Cookies, Headers, Test Results trong khu vực response. Kết quả sẽ hiện ra.
Bước 7 — Với biểu đồ, chuyển sang mô hình pm.getData(). Nhúng CDN của Chart.js, đặt <canvas>, và trong <script> gọi pm.getData(function(err, data){ ... }) để lấy dữ liệu rồi khởi tạo chart.
Bước 8 — Tái sử dụng. Nếu bạn dùng chung một template cho nhiều request, hãy lưu template vào một biến collection hoặc một hàm trong Pre-request/Collection script, rồi gọi lại. Điều này tránh copy-paste template dài dòng qua từng request.
Lỗi thường gặp & mẹo
Tab Visualize không hiện gì. Nguyên nhân phổ biến nhất: bạn quên gọi pm.visualizer.set(...), hoặc gọi nó nhưng request bị lỗi trước đó nên script không chạy tới dòng đó. Kiểm tra tab Console (View → Show Postman Console) để xem lỗi JavaScript.
Dùng {{...}} trong <script> mà không thấy dữ liệu. Như đã nói, Handlebars chỉ điền vào phần HTML tĩnh, không vào code JS chạy động cho chart. Trong JS phải dùng pm.getData(callback).
Gọi sai đường dẫn mảng. Nếu response là { "data": { "orders": [...] } } mà bạn set { orders: body.orders } thì orders là undefined. Phải là body.data.orders. Luôn console.log object trước khi set để chắc chắn.
CSS bị "mất". Visualizer render trong iframe sandbox cô lập, không dùng chung style với Postman. Mọi CSS phải nhúng trong <style> của chính template.
Chart không vẽ được vì chặn script ngoài. Hãy chắc chắn bật cho phép tải tài nguyên ngoài trong Settings của Postman (mục liên quan tới visualizer/CDN). Nếu mạng công ty chặn CDN, cân nhắc nhúng thư viện dạng inline hoặc dùng bảng thay biểu đồ.
Template quá dài khó bảo trì. Mẹo: tách phần tính toán ra khỏi phần hiển thị, và lưu template chung ở cấp collection để nhiều request tái sử dụng. Với dữ liệu lớn hàng nghìn dòng, cân nhắc phân trang hoặc chỉ render top-N, vì render bảng khổng lồ có thể làm Postman giật.
Mẹo escape an toàn: mặc định dùng hai ngoặc để chống vỡ layout và XSS; chỉ ba ngoặc khi thực sự cần HTML.
Bài tập thực hành
- Bảng cơ bản. Lấy một API công khai trả về mảng (ví dụ danh sách người dùng giả lập). Render thành bảng HTML có tiêu đề cột màu, và tô nền xen kẽ các dòng.
- Tô màu điều kiện. Với một API đơn hàng, tô đỏ những dòng có
trang_thai === "failed"và tô xanh những dòng"success". Nhớ tính cờ màu trong JavaScript, không trong template.
- Biểu đồ Chart.js. Gọi một endpoint tổng hợp (hoặc tự tính từ response) để đếm số bản ghi theo nhóm, rồi vẽ biểu đồ cột bằng Chart.js qua
pm.getData().
- Dashboard mini. Kết hợp một thẻ tóm tắt số liệu (tổng số, tỷ lệ pass) ở trên và một bảng chi tiết ở dưới trong cùng một template. Đây là dạng báo cáo bạn có thể chụp màn hình dán vào biên bản họp.
- Xử lý tiếng Việt. Render bảng có tên khách hàng tiếng Việt kèm ghi chú chứa ký tự
<,>. Xác nhận bảng không vỡ khi dùng hai ngoặc, và quan sát sự khác biệt khi cố tình dùng ba ngoặc.
Tóm tắt
Postman Visualizer biến response thô thành bảng, biểu đồ và dashboard HTML tùy biến ngay trong Postman, không cần công cụ ngoài. Trái tim của nó là pm.visualizer.set(template, data), với template dùng engine Handlebars ({{...}}, {{#each}}, {{#if}}). Nguyên tắc vàng: tách logic ra khỏi hiển thị — mọi tính toán, lọc, gắn cờ làm trong JavaScript của tab Tests; template chỉ lo phần vẽ.
Với bảng, dùng {{#each}} và nhúng CSS trong <style>. Với biểu đồ, nhúng Chart.js qua CDN và lấy dữ liệu bằng pm.getData(callback) — chứ không dùng Handlebars trong <script> động. Luôn dùng hai ngoặc {{...}} để escape an toàn dữ liệu, đặc biệt khi có tiếng Việt và ký tự đặc biệt. Nắm vững Visualizer, bạn sẽ biến những báo cáo kiểm thử khô khan thành thứ mà cả sếp không-đọc-code lẫn đồng đội đều hiểu ngay trong vài giây — một kỹ năng nhỏ nhưng nâng tầm sự chuyên nghiệp của người QA.