Product Management
Đăng nhập
ESC

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

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

Migration: Postman → Code-based test (Jest/Mocha)

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

Có một khoảnh khắc mà gần như đội QA nào cũng gặp: bạn mở Postman, click vào collection quen thuộc, và thấy nó đã phình to đến mức phải cuộn chuột mỏi tay mới thấy hết folder. Test chạy chậm. Mỗi lần đổi logic, ba người phải ngồi lại xem "cái pre-request script trong folder Auth đang set biến gì". Không ai dám xóa request nào vì sợ vỡ dây chuyền. Collection từng là công cụ giúp bạn test nhanh, giờ trở thành gánh nặng.

Đây chính là lúc câu hỏi xuất hiện: có nên chuyển sang viết test bằng code (Jest, Mocha) thay vì tiếp tục sống trong Postman không?

Bài này KHÔNG dạy bạn ghét Postman. Postman vẫn là công cụ tuyệt vời để khám phá API, chia sẻ với team, và chạy smoke test nhanh. Nhưng đến một ngưỡng nhất định, việc "code hóa" bộ test sẽ trả lại cho bạn thứ mà Postman khó cho: khả năng maintain, khả năng tái sử dụng logic, khả năng version control tử tế, và tốc độ debug. Học được khi nào migrate và migrate như thế nào cho không đau là một kỹ năng phân biệt giữa một người "biết dùng Postman" và một SDET (Software Development Engineer in Test) thực thụ. Bài này tập trung đúng vào nghệ thuật chuyển giao đó.

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

Migration ở đây nghĩa là gì?

Migration không phải là "xóa Postman đi và viết lại từ đầu". Nó là quá trình chuyển bộ kiểm thử API từ môi trường Postman (collection + script trong tab Tests) sang một dự án code chạy độc lập — thường là một project Node.js dùng test runner như Jest hoặc Mocha, kèm thư viện gọi HTTP như axios, supertest hay node-fetch, và thư viện assertion như chai hoặc expect có sẵn của Jest.

Điểm mấu chốt: trong Postman, request và assertion bị "khóa" trong định dạng JSON của collection. Còn khi ở dạng code, mỗi test case là một hàm JavaScript bình thường — bạn được dùng toàn bộ sức mạnh của ngôn ngữ: vòng lặp thật, hàm dùng chung, module hóa, import npm package, và debug bằng breakpoint.

Những dấu hiệu (triggers) cho biết đã đến lúc migrate

Không phải cứ có collection là phải migrate. Hãy nhìn vào các dấu hiệu cụ thể sau:

  • Collection vượt ngưỡng quy mô (thường > 150–200 request). Ở quy mô này, việc tìm một request, hiểu luồng chạy, hay refactor trở nên cực kỳ tốn công. Code cho phép chia file, đặt tên hàm, tra cứu bằng "Find in files".
  • Logic phức tạp — state machine, async chain nhiều bước, điều kiện rẽ nhánh. Script trong tab Tests của Postman là JavaScript, nhưng nó chạy trong một sandbox hạn chế, khó tái sử dụng giữa các request, và không có kiểu (type) hay công cụ lint. Khi bạn phải viết một workflow như "đăng nhập → tạo đơn → thanh toán → hủy → kiểm tra hoàn tiền", việc quản lý biến trạng thái qua nhiều tab script rất dễ sai.
  • Cần tái sử dụng code mạnh. Ví dụ một hàm generateSignature() cho VNPay, hay một helper buildAuthHeader(). Trong Postman bạn phải nhét chúng vào biến collection dưới dạng chuỗi hàm — xấu và khó debug. Trong code chỉ là một file helpers/signature.js import bình thường.
  • Cần version control và code review nghiêm túc. Collection Postman là một file JSON khổng lồ; khi hai người cùng sửa và merge trên Git, diff gần như không đọc nổi. Code thì review từng dòng dễ dàng.
  • Cần tích hợp sâu với hệ sinh thái dev. Muốn dùng chung fixture với unit test của backend, muốn mock database, muốn chạy test trong cùng pipeline với npm test — code hòa nhập tự nhiên hơn.

Những dấu hiệu cho biết CHƯA nên migrate

Cân bằng lại: nếu team bạn gồm nhiều QA không code, nếu bộ test nhỏ (< 50 request đơn giản), nếu giá trị chính là để BA/PO xem và chạy thử API — thì ở lại Postman là lựa chọn đúng. Migrate sớm quá là tự bắn vào chân: bạn đánh đổi sự dễ tiếp cận lấy một thứ mà chưa ai trong team sẵn sàng bảo trì.

Bản đồ ánh xạ khái niệm Postman ↔ Code

Hiểu migration dễ hơn nhiều khi bạn thấy mọi thứ trong Postman đều có "bản sao" bên code:

PostmanCode (Jest/Mocha)
RequestLời gọi axios.get()/post()
pm.test("...", () => {...})test("...", () => {...}) / it(...)
pm.expect(x).to.eql(y)expect(x).toBe(y) (Jest) hoặc chai.expect
Environment variableBiến trong file .env + process.env
Collection variableBiến module / config object
Pre-request scriptCode chạy trong beforeEach/beforeAll
Tests tabPhần assertion sau await
Newman runnpm test / jest / mocha

Tình huống thực tế

Ví dụ 1 — Tiki-style: collection 300 request "quá tải"

Một team QA tại một sàn thương mại điện tử ở TP.HCM (gọi là "ShopViet") có collection Postman với hơn 300 request, tích lũy suốt 2 năm. Ban đầu ổn, nhưng đến giai đoạn sản phẩm có tới 8 microservice, mỗi lần backend đổi contract là QA phải mở Postman, dò từng folder, sửa tay hàng chục request. Một lần release, họ bỏ sót không cập nhật 4 request và để lọt bug thanh toán ra production — thiệt hại ước tính vài chục triệu đồng tiền hoàn và uy tín.

Họ quyết định migrate dần sang Jest + axios. Kết quả sau 6 tuần: bộ test còn 40 file .test.js được nhóm theo service, các assertion trùng lặp được gom vào helper dùng chung, và thời gian chạy full suite giảm từ ~9 phút (Newman) xuống ~3 phút (Jest chạy song song). Quan trọng hơn: khi contract đổi, họ sửa một helper thay vì 30 request.

Bài học: quy mô và tần suất thay đổi là lý do migrate chính đáng nhất. Sự lặp lại trong Postman là chi phí ẩn — chỉ lộ ra khi có sự cố.

Ví dụ 2 — Fintech: workflow async quá phức tạp cho sandbox Postman

Một startup fintech ở Hà Nội test luồng ví điện tử: tạo giao dịch → chờ callback bất đồng bộ từ đối tác ngân hàng → poll trạng thái mỗi 2 giây tối đa 30 giây → xác nhận → đối soát. Trong Postman, họ phải dựng một chuỗi request "tự gọi lại chính nó" bằng postman.setNextRequest() kèm biến đếm, và logic poll nằm rải rác trong nhiều tab Tests. Mỗi lần debug là một cơn ác mộng vì không đặt được breakpoint, chỉ có console.log trong Postman Console.

Khi chuyển sang Mocha, cả workflow gói gọn trong một hàm async với while loop và await sleep(2000) rõ ràng, dễ đọc như văn xuôi. Họ còn viết được test cho các nhánh lỗi (callback timeout, callback sai chữ ký) mà trước đây gần như không thể mô phỏng trong Postman.

Bài học: khi logic là async nhiều bước hoặc state machine, code cho bạn cấu trúc điều khiển thật (loop, try/catch, Promise) — thứ mà chuỗi request Postman chỉ mô phỏng vụng về.

Ví dụ 3 — Migrate SAI thời điểm: bài học ngược

Một team 4 người, trong đó 3 là manual tester không biết JavaScript, bị một tech lead mới ép migrate toàn bộ 60 request sang Jest "cho chuyên nghiệp". Ba tháng sau, chỉ có tech lead đó động vào được bộ test; ba QA còn lại quay về... test tay vì không dám sửa code. Bộ test mục ruỗng dần, coverage giảm.

Bài học: migration là quyết định về con người và bối cảnh, không chỉ về công nghệ. Nếu team chưa sẵn sàng code, hãy đầu tư đào tạo trước, hoặc migrate từng phần nhỏ, hoặc đơn giản là ở lại Postman. Công cụ "xịn" hơn mà không ai dùng được thì tệ hơn công cụ "thường" mà cả team làm chủ.

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

Dưới đây là quy trình migrate an toàn, làm dần chứ không "big bang".

Bước 1 — Đánh giá và ưu tiên. Liệt kê các folder trong collection theo giá trị và độ ổn định. Migrate trước những luồng quan trọng nhất và ít thay đổi nhất (ví dụ: auth, checkout). Đừng migrate hết một lượt.

Bước 2 — Dựng khung dự án.

mkdir api-tests && cd api-tests
npm init -y
npm i -D jest axios dotenv

Tạo .env để thay cho Postman environment:

BASE_URL=https://staging-api.shopviet.vn
API_KEY=your_staging_key

Bước 3 — Tạo một HTTP client dùng chung. Đây là bản "environment + base URL" của bạn:

// src/client.js
require('dotenv').config();
const axios = require('axios');

const client = axios.create({ baseURL: process.env.BASE_URL, headers: { 'X-API-Key': process.env.API_KEY }, validateStatus: () => true, // tự kiểm tra status trong test });

module.exports = client;

Bước 4 — Chuyển một request đầu tiên. Lấy một request Postman và dịch sang test. Ví dụ request "GET /products/:id" với assertion status 200 và có field name:

// tests/products.test.js
const client = require('../src/client');

describe('Products API', () => { test('GET /products/:id trả về sản phẩm hợp lệ', async () => { const res = await client.get('/products/1001');

expect(res.status).toBe(200); // pm.response.to.have.status(200) expect(res.data).toHaveProperty('name'); expect(res.data.price).toBeGreaterThan(0); }); });

Bước 5 — Chuyển pre-request script thành beforeAll/beforeEach. Ví dụ Postman đăng nhập lấy token trong pre-request, bên code:

let token;
beforeAll(async () => {
  const res = await client.post('/auth/login', {
    email: 'test@shopviet.vn', password: 'secret',
  });
  token = res.data.accessToken;
  client.defaults.headers.common['Authorization'] = Bearer ${token};
});

Bước 6 — Gom logic lặp lại vào helper. Hàm ký VNPay, tạo dữ liệu giả, kiểm tra schema... đặt vào src/helpers/. Đây chính là giá trị lớn nhất của migration.

Bước 7 — Chạy và tích hợp. Thêm "test": "jest" vào package.json, chạy npm test. Sau đó cắm thẳng vào CI (bài về CI/CD trong khóa sẽ đi sâu). Chạy song song hai hệ (Postman cũ + Jest mới) trong vài sprint để yên tâm không sót coverage, rồi mới "nghỉ hưu" collection cũ.

Bước 8 — Xác thực tương đương. Trước khi bỏ collection, đối chiếu: mọi assertion trong Postman đã có bản sao trong code chưa? Dùng checklist, đừng tin trí nhớ.

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

  • Migrate "big bang" toàn bộ một lần. Đây là sai lầm phổ biến nhất và dễ khiến dự án chết giữa chừng. Hãy làm theo folder, chạy song song hai hệ trong giai đoạn chuyển tiếp.
  • Quên xử lý biến động (dynamic variables) của Postman. Những {{$randomEmail}}, {{$timestamp}} trong Postman cần được thay bằng thư viện như @faker-js/faker hoặc Date.now(). Rà soát kỹ, kẻo test tạo dữ liệu trùng.
  • Hardcode secret vào code. Postman environment che secret khá tiện; khi sang code, nhiều người copy luôn key vào file test. Dùng .env + đưa .env vào .gitignore. Đừng bao giờ commit key.
  • Bỏ qua validateStatus của axios. Mặc định axios ném lỗi khi status ≥ 400, làm test lỗi 4xx/5xx bị crash thay vì assert. Set validateStatus: () => true rồi tự kiểm tra status như trong ví dụ.
  • Không tái tạo thứ tự chạy. Trong Postman, request chạy tuần tự theo thứ tự trong folder. Jest chạy các file song song và không đảm bảo thứ tự giữa các test(). Nếu có phụ thuộc trạng thái, dùng --runInBand, hoặc tốt hơn: thiết kế test độc lập, tự tạo dữ liệu của mình.
  • Mẹo — tận dụng chính Postman để khởi động. Postman có nút "Code snippet" (biểu tượng </>) sinh sẵn đoạn code axios/fetch cho mỗi request. Dùng nó làm khung, rồi bọc assertion vào — tiết kiệm rất nhiều thời gian gõ tay.
  • Mẹo — giữ Postman cho việc khám phá. Sau migration, đừng vứt Postman. Nó vẫn là nơi tuyệt vời để thử API mới trước khi viết test code. Migration là chuyển bộ test tự động, không phải cấm dùng công cụ.

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

  • Đánh giá triggers. Lấy một collection thật (hoặc một collection mẫu bạn tự tạo ~30 request). Viết một bảng ngắn chấm điểm 5 tiêu chí: quy mô, độ phức tạp logic, nhu cầu tái sử dụng, nhu cầu version control, năng lực code của team. Kết luận: nên migrate hay ở lại? Vì sao?
  • Dựng khung dự án. Tạo project Node.js với Jest + axios + dotenv theo Bước 2–3. Cấu hình .env trỏ tới một API công khai (ví dụ https://reqres.in hoặc https://jsonplaceholder.typicode.com).
  • Chuyển 3 request. Chọn 3 request từ collection và dịch sang 3 test case Jest, bao gồm ít nhất một request cần token (dùng beforeAll) và một assertion kiểm tra cấu trúc response.
  • Rút helper. Tìm một đoạn logic lặp lại ở ít nhất 2 test (ví dụ kiểm tra format response chung) và tách thành một hàm trong src/helpers/. Import và dùng lại.
  • Chạy song song. Chạy npm test và đối chiếu kết quả với lần chạy Newman của cùng các request đó. Lập checklist xác nhận mọi assertion đã được tái tạo đầy đủ.

Tóm tắt

Migration từ Postman sang code-based test (Jest/Mocha) không phải là "chê" Postman, mà là chọn đúng công cụ cho đúng quy mô. Hãy migrate khi collection đã quá lớn (thường > 150–200 request), khi logic phức tạp kiểu async chain hay state machine, khi bạn cần tái sử dụng code mạnh, version control tử tế và tích hợp sâu với hệ sinh thái dev. Ngược lại, nếu team chưa sẵn sàng code hoặc bộ test còn nhỏ, ở lại Postman là quyết định khôn ngoan.

Khi đã quyết migrate, hãy làm dần theo folder, chạy song song hai hệ trong giai đoạn chuyển tiếp, ánh xạ từng khái niệm Postman sang code (request → axios, pm.test → test, environment → .env, pre-request → beforeAll), và dồn giá trị vào việc gom logic lặp lại thành helper dùng chung. Tránh các bẫy kinh điển: big bang, hardcode secret, quên validateStatus, và bỏ qua thứ tự chạy. Nhớ rằng migration thành công đo bằng việc cả team làm chủ được bộ test mới — không phải bằng độ "xịn" của công nghệ. Đó chính là tư duy của một API Tester đang trên đường trở thành SDET.

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