Product Management
Đăng nhập
ESC

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

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

Newman với Docker — containerized run

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

Hãy tưởng tượng bạn vừa viết xong một bộ test API bằng Postman rất đẹp, chạy trên máy bạn thì "xanh lè" hết. Nhưng khi đưa lên máy đồng nghiệp, hoặc lên server CI, thì hàng loạt lỗi lạ xuất hiện: "newman: command not found", "Node version không tương thích", "thiếu reporter htmlextra"... Bạn mất cả buổi chiều chỉ để cài lại môi trường cho khớp. Câu chuyện quen thuộc này chính là lý do Docker ra đời, và cũng là lý do bài học này tồn tại.

Trong toàn bộ hành trình học Newman của chúng ta, các bài trước đã dạy bạn chạy Newman trực tiếp trên máy (bài 13), tạo report đẹp (bài 14), và tích hợp vào GitHub Actions, GitLab, Jenkins (bài 16, 17). Bài 28 này giải quyết một vấn đề rất cụ thể và mang tính nền tảng: làm sao để đóng gói Newman cùng toàn bộ môi trường của nó vào một container, để nó chạy y hệt nhau ở mọi nơi — máy bạn, máy đồng nghiệp, server CI, hay cloud. Đây không phải là "học thêm cho vui", mà là kỹ năng bắt buộc nếu bạn muốn bộ test của mình đáng tin cậy và tái lập được (reproducible).

Container hóa Newman là mảnh ghép mà rất nhiều QA bỏ quên, dẫn đến cảnh "chạy trên máy tôi thì được" kéo dài triền miên. Học xong bài này, bạn sẽ không bao giờ phải nói câu đó nữa.

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

Container là gì và tại sao Newman cần nó

Nói đơn giản, container là một "hộp" chứa đầy đủ mọi thứ mà chương trình cần để chạy: hệ điều hành thu nhỏ, Node.js đúng phiên bản, Newman đúng version, các reporter đã cài sẵn. Bạn gửi cái hộp này cho ai, họ mở ra là chạy được ngay, không cần cài gì thêm. Khác với việc cài Newman trực tiếp lên máy (nơi phụ thuộc vào Node.js có sẵn của máy đó), container mang theo cả Node.js của riêng nó.

Postman phát hành sẵn một official image (ảnh container chính thức) tên là postman/newman. Bạn không cần tự xây dựng gì cả, chỉ cần kéo về:

docker pull postman/newman

Các tag của image postman/newman

Đây là điểm nhiều người bỏ sót. Image postman/newman có nhiều tag (nhãn phiên bản), và chọn đúng tag rất quan trọng:

  • postman/newman:latest hoặc postman/newman:alpine — bản gọn nhẹ dựa trên Alpine Linux, dung lượng nhỏ (~200MB), chạy nhanh, phù hợp CI.
  • postman/newman:ubuntu — bản đầy đủ dựa trên Ubuntu, nặng hơn nhưng có sẵn nhiều thư viện hệ thống, hữu ích khi test cần thư viện native.
  • postman/newman:6-alpine, postman/newman:5-alpine — cố định theo major version của Newman. Lời khuyên vàng: luôn ghim (pin) một version cụ thể trong môi trường production/CI, đừng dùng latest, vì latest có thể đổi bất ngờ và làm hỏng pipeline của bạn vào một sáng thứ Hai đẹp trời.

Volume — cầu nối giữa host và container

Container mặc định là một thế giới cô lập, không thấy file trên máy bạn. Nhưng collection và environment của bạn nằm trên máy (host). Làm sao đưa chúng vào container? Câu trả lời là volume mount — ghép một thư mục trên máy vào một thư mục bên trong container.

Image postman/newman được thiết kế để làm việc với thư mục /etc/newman bên trong container. Khi bạn mount thư mục hiện tại vào đó, Newman sẽ tìm thấy file collection:

docker run -v $(pwd):/etc/newman -t postman/newman \
  run my-collection.json

Giải thích từng phần:

  • docker run — chạy một container mới.
  • -v $(pwd):/etc/newman — mount thư mục hiện tại ($(pwd) là "print working directory") vào /etc/newman trong container.
  • -t — cấp một terminal giả để output có màu, dễ đọc.
  • postman/newman — tên image.
  • run my-collection.json — phần này là lệnh Newman thật sự. Lưu ý: entrypoint của image đã là newman rồi, nên bạn không gõ lại chữ newman, chỉ gõ run ... trở đi.
Vì working directory mặc định trong container đã là /etc/newman, nên my-collection.json được hiểu là file nằm ngay trong thư mục bạn đã mount. Rất gọn.

Entrypoint — vì sao không gõ chữ "newman"

Đây là lỗi kinh điển của người mới. Nhiều bạn viết docker run postman/newman newman run ... và bị lỗi. Lý do: image đã đặt ENTRYPOINT ["newman"], nghĩa là mọi thứ bạn truyền vào sau tên image tự động được nối vào sau chữ newman. Nếu bạn gõ thêm newman nữa, lệnh thực tế thành newman newman run ... — sai bét. Chỉ cần gõ run ... là đủ.

Tình huống thực tế

Ví dụ 1: Tiki chuẩn hóa môi trường test giữa 8 QA

Một đội QA (giả định theo bối cảnh sàn thương mại điện tử như Tiki) có 8 thành viên, dùng đủ loại máy: MacBook M2, laptop Windows, cả Ubuntu. Trước đây mỗi lần onboard một QA mới, họ mất trung bình nửa ngày để cài Node.js đúng version, cài Newman, cài newman-reporter-htmlextra. Tệ hơn, có bạn dùng Node 18, có bạn Node 20, và một test liên quan đến xử lý timezone chạy khác nhau giữa các máy — gây ra "flaky test" (test lúc pass lúc fail) khiến cả đội nghi ngờ chất lượng bộ test.

Giải pháp: họ đóng gói tất cả vào một lệnh Docker duy nhất, lưu trong file run-tests.sh:

docker run --rm -v $(pwd):/etc/newman \
  postman/newman:6.1-alpine \
  run collections/tiki-checkout.json \
  -e environments/staging.json \
  -r cli,htmlextra

Kết quả: thời gian onboard giảm từ nửa ngày xuống còn 5 phút (thời gian kéo image lần đầu). Flaky test biến mất vì mọi người dùng chung một Node version đóng gói sẵn trong image. Bài học rút ra: container biến "môi trường" từ thứ mỗi người tự dựng thành thứ được version-control và chia sẻ.

Lưu ý cờ --rm: nó tự xóa container sau khi chạy xong, tránh rác container tích tụ đầy máy — một thói quen tốt bắt buộc phải có.

Ví dụ 2: Một fintech Đông Nam Á và bài toán reporter chưa cài

Một công ty fintech (giả định, làm ví điện tử) muốn dùng reporter Allure (bài 14) để tích hợp báo cáo test API vào dashboard chung. Nhưng khi họ chạy postman/newman với -r allure, container báo lỗi vì reporter Allure không có sẵn trong official image. Đây là giới hạn quan trọng: official image chỉ có sẵn cli, json, junit, và htmlextra (ở các bản mới). Các reporter khác phải tự thêm.

Họ có hai lựa chọn. Cách nhanh (cho lần chạy đơn lẻ) là cài reporter ngay trước khi chạy bằng cách override entrypoint:

docker run --rm -v $(pwd):/etc/newman \
  --entrypoint sh postman/newman:alpine \
  -c "npm install -g newman-reporter-allure && \
      newman run collection.json -r allure"

Cách bền vững (dùng lâu dài) là tự viết một Dockerfile mở rộng từ image gốc, cài sẵn reporter, rồi build thành image riêng của công ty:

FROM postman/newman:6-alpine
RUN npm install -g newman-reporter-allure

Sau đó build và đặt tên: docker build -t fintech-newman:1.0 ., rồi dùng fintech-newman:1.0 thay cho postman/newman. Bài học: official image là điểm khởi đầu, không phải điểm kết thúc — khi cần thư viện đặc thù, hãy xây image riêng và đưa nó lên registry nội bộ (như Docker Hub private hoặc GitLab Container Registry) để cả đội dùng chung.

Ví dụ 3: Chạy trong CI với Docker và mã thoát (exit code)

Một startup logistics ở TP.HCM tích hợp Newman-trong-Docker vào GitLab CI. Ban đầu họ gặp vấn đề: dù có test fail, pipeline vẫn báo "success" màu xanh, che giấu lỗi thật. Nguyên nhân: họ chạy container trong một script bao ngoài "nuốt" mất exit code.

Điểm mấu chốt: Newman trả về exit code khác 0 khi có test fail, và Docker "chuyển tiếp" (propagate) exit code đó ra ngoài. Nghĩa là nếu bạn chạy đúng cách, docker run ... sẽ tự fail khi test fail, và CI sẽ đỏ như mong đợi. Họ sửa lại đoạn .gitlab-ci.yml:

api-test:
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker run --rm -v $(pwd):/etc/newman
        postman/newman:6-alpine
        run collections/orders.json -e environments/prod.json

Sau khi bỏ lớp wrapper thừa, pipeline bắt đầu đỏ đúng lúc test fail. Bài học: giá trị của test tự động nằm ở việc nó dám báo đỏ. Container không làm hỏng cơ chế exit code — chỉ cần đừng vô tình chặn nó bằng || true hay wrapper cẩu thả.

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

Hãy cùng đi từ số 0 đến chạy được một collection trong Docker.

Bước 1 — Cài Docker. Trên máy cá nhân, cài Docker Desktop (Mac/Windows) hoặc Docker Engine (Linux). Kiểm tra: docker --version. Nếu ra số version là ổn.

Bước 2 — Kéo image về. Chạy docker pull postman/newman:6-alpine. Lần đầu sẽ tải khoảng 200MB, các lần sau dùng lại cache nên tức thì.

Bước 3 — Chuẩn bị file. Xuất (export) collection và environment từ Postman ra file .json, đặt chúng trong một thư mục, ví dụ postman-tests/. Cấu trúc gợi ý:

postman-tests/
  collections/my-api.json
  environments/staging.json
  data/users.csv

Bước 4 — Chạy lần đầu. Từ trong thư mục postman-tests/:

docker run --rm -t -v $(pwd):/etc/newman \
  postman/newman:6-alpine \
  run collections/my-api.json \
  -e environments/staging.json
Bạn sẽ thấy output test quen thuộc của Newman ngay trong terminal.

Bước 5 — Thêm data-driven (nếu cần). Nếu bộ test của bạn dùng file CSV để lặp (bài 15), thêm cờ -d:

  run collections/my-api.json -e environments/staging.json \
  -d data/users.csv
Vì file CSV cũng nằm trong thư mục đã mount, container đọc được bình thường.

Bước 6 — Xuất report ra file. Muốn report HTML nằm lại trên máy bạn (không mất khi container xóa), hãy xuất vào thư mục đã mount:

  run collections/my-api.json -e environments/staging.json \
  -r cli,htmlextra \
  --reporter-htmlextra-export reports/result.html
Sau khi chạy, file reports/result.html xuất hiện ngay trên máy host, mở bằng trình duyệt xem được. Đây là mấu chốt: report phải ghi vào thư mục mount, nếu ghi ra chỗ khác trong container thì nó biến mất cùng container.

Bước 7 — Đưa vào CI. Copy chính xác lệnh Docker này vào file cấu hình CI (GitHub Actions, GitLab, Jenkins). Vì lệnh đã tự chứa mọi thứ, nó chạy giống hệt trên CI như trên máy bạn — đó chính là lời hứa của container.

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

Lỗi 1: "could not find file collection.json". Gần như luôn là do volume mount sai đường dẫn, hoặc bạn chạy lệnh không đứng trong thư mục chứa file. Kiểm tra $(pwd) có trỏ đúng chỗ không. Trên Windows CMD, $(pwd) không hoạt động — dùng %cd% thay thế, hoặc dùng PowerShell với ${PWD}.

Lỗi 2: File report ghi xong không thấy trên máy. Bạn đã xuất report vào một đường dẫn không nằm trong thư mục mount. Report chỉ tồn tại nếu ghi vào bên trong /etc/newman (tức thư mục đã mount ra host).

Lỗi 3: "newman newman run..." báo lỗi cú pháp. Bạn gõ dư chữ newman do quên entrypoint đã là newman. Bỏ chữ đó đi.

Lỗi 4: Report HTML bị lỗi quyền (permission denied). Xảy ra trên Linux khi container tạo file với user khác. Thêm cờ --user $(id -u):$(id -g) để container chạy với user của bạn, file tạo ra sẽ đúng quyền sở hữu.

Lỗi 5: Vấn đề tiếng Việt / Unicode trong data. Nếu file CSV có tiếng Việt (bài 35 nói kỹ về encoding), đảm bảo file lưu dạng UTF-8. Bản Alpine đôi khi thiếu locale — nếu gặp lỗi hiển thị ký tự, thử bản postman/newman:ubuntu vốn đầy đủ locale hơn.

Mẹo 1: Luôn dùng --rm. Tránh để lại container "xác chết" chiếm dung lượng.

Mẹo 2: Ghim version image. Đừng dùng latest trong CI. Dùng postman/newman:6.1-alpine chẳng hạn, để pipeline không tự vỡ khi Postman đẩy bản mới.

Mẹo 3: Tạo alias hoặc script. Lệnh Docker khá dài. Gói nó vào file run-tests.sh hoặc alias shell để gõ một lệnh ngắn là chạy.

Mẹo 4: Cache image trong CI. Nhiều hệ CI kéo lại image mỗi lần chạy. Cấu hình cache Docker layer để tiết kiệm thời gian và băng thông, đặc biệt quan trọng khi đội chạy hàng trăm pipeline mỗi ngày.

Mẹo 5: Đọc log khi CI đỏ. Vì container cô lập, khi lỗi lạ, hãy chạy lại y hệt lệnh đó trên máy bạn để tái hiện — đây chính là siêu năng lực của Docker: lỗi trên CI tái hiện được y hệt trên máy local.

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

  • Khởi động: Kéo image postman/newman:alpine về máy. Xuất một collection bất kỳ từ Postman ra JSON, rồi chạy nó trong container bằng volume mount. Xác nhận output test hiển thị đúng.
  • Xuất report ra host: Thêm reporter htmlextra, xuất file HTML vào thư mục con reports/. Sau khi container tự xóa (--rm), kiểm tra file HTML vẫn còn trên máy và mở được.
  • Data-driven trong container: Tạo một file CSV 3 dòng dữ liệu (có ít nhất một cột chứa tiếng Việt có dấu). Chạy collection với cờ -d, xác nhận Newman lặp đúng 3 lần và ký tự tiếng Việt hiển thị đúng.
  • Xây image riêng: Viết một Dockerfile mở rộng từ postman/newman:6-alpine, cài thêm một reporter bất kỳ (ví dụ newman-reporter-htmlextra nếu bản của bạn chưa có). Build thành image tên my-newman:1.0 và chạy thử.
  • Kiểm tra exit code: Cố tình sửa một test cho fail. Chạy container, rồi gõ echo $? ngay sau đó (trên Linux/Mac) để xem exit code. Xác nhận nó khác 0 — đây là cơ chế giúp CI biết test đã fail.

Tóm tắt

Newman trong Docker giải quyết bài toán muôn thuở "chạy trên máy tôi thì được": nó đóng gói Newman, Node.js và mọi phụ thuộc vào một container tái lập được ở mọi nơi. Những điểm cốt lõi cần nhớ:

  • Official imagepostman/newman, kéo về bằng docker pull. Luôn ghim version cụ thể (như 6-alpine) thay vì latest trong môi trường quan trọng.
  • Volume mount (-v $(pwd):/etc/newman) là cầu nối đưa collection, environment, data file vào container — và cũng là nơi report phải ghi ra để không biến mất.
  • Entrypoint đã là newman, nên chỉ gõ run ... chứ đừng lặp lại chữ newman.
  • Khi cần reporter đặc thù không có sẵn, hãy mở rộng bằng Dockerfile riêng và đẩy lên registry nội bộ để cả đội dùng chung.
  • Exit code được Docker chuyển tiếp nguyên vẹn — đừng nuốt nó bằng wrapper cẩu thả, để CI dám báo đỏ khi test fail.
  • Luôn dùng --rm để dọn container, và biết vài mẹo xử lý quyền file (--user), Unicode tiếng Việt (bản ubuntu), và cache image trong CI.
Nắm vững container hóa Newman, bạn đã biến bộ test API từ thứ "chạy được ở đâu tùy hên xui" thành một tài sản kỹ thuật chuyên nghiệp, tái lập, và sẵn sàng cho mọi pipeline. Ở các bài tiếp theo về architecture bộ test (bài 51) và migration sang code-based (bài 52), nền tảng container này sẽ còn phát huy giá trị.

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