Tự động hóa kiểm thử API trong CI/CD với Postman và Newman: 6 tháng thực tế triển khai

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

Thảm họa triển khai lúc 5 giờ chiều thứ Sáu

Sáu tháng trước, đội ngũ tám kỹ sư của chúng tôi luôn sống trong nỗi sợ hãi mang tên “triển khai chiều thứ Sáu“. Chúng tôi quản lý 42 microservices và hơn 150 endpoints. Các unit tests của chúng tôi luôn báo xanh, thế nhưng môi trường staging lại gặp lỗi gần như mỗi tuần. Một key JSON bị đổi tên, một mã HTTP status thay đổi, hay một header bị thiếu—những thay đổi nhỏ này đã lọt qua unit tests vì chúng chỉ kiểm tra logic trong môi trường cô lập, chứ không phải hợp đồng (contract) thực tế giữa các dịch vụ.

Một sự cố cụ thể vẫn còn in đậm trong tâm trí. Một cập nhật nhỏ cho dịch vụ Auth đã khiến toàn bộ luồng Checkout bị treo, dẫn đến tỷ lệ chuyển đổi giảm 12% trước khi chúng tôi kịp nhận ra. Các unit tests vẫn pass vì logic tạo token vẫn ổn.

Tuy nhiên, cấu trúc phản hồi của API đã thay đổi nhẹ, và frontend không thể parse được user ID. Chúng tôi đã mất 4,5 giờ trong một đêm thứ Sáu để rollback và debug. Đó là giới hạn chịu đựng cuối cùng. Chúng tôi phải xác thực các API như một hệ thống tích hợp, đang hoạt động trong suốt quá trình triển khai.

Tại sao các lỗi hồi quy API vẫn tiếp tục lọt lưới

Sau buổi họp rút kinh nghiệm (post-mortem), tôi đã chỉ ra ba lỗ hổng chính trong quy trình làm việc. Thứ nhất, kiểm thử thủ công đóng vai trò như một mỏ neo kìm hãm. Trưởng nhóm QA của chúng tôi có một bộ Postman collections tuyệt vời, nhưng việc chạy chúng thủ công cho mọi pull request là điều bất khả thi. Nó vừa chậm vừa phụ thuộc vào con người. Khi deadline cận kề, đó luôn là thứ đầu tiên bị chúng tôi cắt giảm.

Thứ hai, các nhà phát triển và vận hành đang nói những ngôn ngữ khác nhau. Dev sử dụng Postman để debug cục bộ, nhưng các bài test đó chỉ nằm trên máy tính cá nhân của họ. Luồng CI/CD của chúng tôi, do đội DevOps quản lý, hoàn toàn không thấy được các bài test này. Kiến thức về cách API nên hoạt động bị khóa chặt bên trong một giao diện UI thay vì được commit vào codebase.

Thứ ba, các bài test end-to-end của chúng tôi quá nặng nề. Chúng tôi đã thử Selenium, nhưng các bài test thường xuyên bị lỗi (flaky) và mất tới 25 phút để chạy. Chúng tôi cần một công cụ mang tính “phẫu thuật” cho lớp API. Nó phải đủ nhanh để chạy trên mọi commit nhưng đủ sâu để bắt được các thay đổi gây lỗi trong chu kỳ request/response.

So sánh các lựa chọn: Thủ công, Script, hay Newman?

Chúng tôi đã cân nhắc ba con đường trước khi quyết định. Duy trì chạy Postman thủ công là không thể; nếu một bài test không được tự động hóa, nó coi như không tồn tại trong môi trường DevOps hiện đại. Đơn giản là nó không thể mở rộng.

Tiếp theo, chúng tôi đã cân nhắc việc viết lại mọi thứ bằng Pytest hoặc Jest. Mặc dù các framework lập trình mang lại sức mạnh lớn, chúng lại tạo ra công việc chồng chéo. Các dev của chúng tôi vốn đã dùng Postman để xây dựng API. Việc ép họ duy trì một bộ test riêng biệt bằng ngôn ngữ khác giống như một gánh nặng lên năng suất. Điều đó cũng có nghĩa là đội QA của chúng tôi không thể đóng góp nếu không học sâu về JavaScript hoặc Python.

Và rồi Newman xuất hiện. Newman là phiên bản dòng lệnh tương đương với trình chạy (runner) của Postman. Nó thực thi chính các collections mà bạn xây dựng trên giao diện UI nhưng thực hiện từ terminal. Sau một tuần thử nghiệm, Newman trở thành mảnh ghép còn thiếu của chúng tôi. Nó kết hợp giao diện UI trực quan của Postman để tạo test với sức mạnh CLI cần thiết cho một pipeline tự động hoàn toàn.

Bản thiết kế: Postman + Newman trong CI/CD

Làm chủ quy trình này là sự khác biệt giữa việc ship code một cách mù quáng và bàn giao một sản phẩm đã được xác thực. Hệ thống mà tôi đã tinh chỉnh trong 180 ngày qua tuân theo một mô hình nghiêm ngặt: “export, execute, và integrate” (xuất file, thực thi và tích hợp).

Bước 1: Củng cố các Postman Collections

Assertions (kiểm tra điều kiện) là bắt buộc. Bạn không thể chỉ kiểm tra xem có phản hồi hay không; bạn phải xác thực dữ liệu. Trong Postman, chúng tôi sử dụng tab ‘Tests’. Mọi request trong bộ test của chúng tôi giờ đây đều xác thực status code và JSON schema.

// Các Assertion API thiết yếu
pm.test("Xác minh trạng thái 200 OK", function () {
    pm.response.to.have.status(200);
});

pm.test("Xác thực tính toàn vẹn của Schema", function () {
    const jsonData = pm.response.json();
    pm.expect(jsonData).to.have.property('id');
    pm.expect(jsonData.status).to.eql('active');
});

Xuất các Collections và biến môi trường (Environment) này thành các file JSON. Chúng tôi lưu trữ chúng trong thư mục /tests/api bên trong repo của ứng dụng. Điều này đảm bảo rằng khi một kỹ sư thay đổi hợp đồng API, họ sẽ cập nhật bài test trong cùng một commit.

Bước 2: Xác thực cục bộ với Newman

Trước khi push lên Git, chúng tôi chạy test cục bộ để đảm bảo không có lỗi hồi quy. Newman yêu cầu Node.js và có thể được cài đặt qua npm.

# Cài đặt Newman
npm install -g newman

# Thực thi collection
newman run ./tests/api/orders_api.json -e ./tests/api/staging_env.json

Newman trả về một mã exit code khác không khi thất bại. Đây chính là “nút dừng khẩn cấp” (kill switch). Nếu một bài test thất bại, exit code sẽ là 1, và luồng CI/CD sẽ dừng ngay lập tức, ngăn chặn code lỗi tiếp cận người dùng.

Bước 3: Tích hợp GitHub Actions

Chúng tôi đã cấu hình GitHub Actions để kích hoạt các bài test API ngay khi code được triển khai lên staging. Đây là cấu hình YAML chính xác mà chúng tôi sử dụng.

name: Kiểm thử tích hợp API
on: [push]

jobs:
  test-api:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Cài đặt Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'

      - name: Cài đặt Newman
        run: npm install -g newman

      - name: Thực thi Smoke Tests
        run: |
          newman run ./tests/api/smoke_tests.json \
          --env-var "base_url=${{ secrets.STAGING_URL }}" \
          --reporters cli,junit \
          --reporter-junit-export results.xml

Hãy chú ý cờ --env-var. Chúng tôi sử dụng cờ này để chèn các bí mật (secrets) và URL động. Điều này cho phép cùng một file collection hoạt động trên các môi trường local, staging, và production.

Những bài học xương máu từ thực tế

Sau hơn 12.000 lượt chạy tự động, tôi nhận thấy rằng tài liệu cơ bản là không đủ. Thứ nhất, báo cáo rất quan trọng. Các lập trình viên thích kết quả trên CLI, nhưng các bên liên quan cần hình ảnh trực quan. Chúng tôi sử dụng plugin newman-reporter-htmlextra. Nó tạo ra các báo cáo HTML tương tác bao gồm đầy đủ logs request/response cho các bài test thất bại, giúp chúng tôi tiết kiệm ít nhất 30 phút debug cho mỗi lần lỗi.

# Cài đặt reporter nâng cao
npm install -g newman-reporter-htmlextra

# Chạy với đầu ra trực quan
newman run collection.json -r cli,htmlextra --reporter-htmlextra-export ./reports/api_report.html

Thứ hai, hãy bảo vệ các bí mật của bạn. Đừng bao giờ commit API keys trong file JSON Postman Environment của bạn. Hãy sử dụng các trình giữ chỗ như {{ADMIN_KEY}} và chèn các giá trị thực thông qua GitHub Secrets khi chạy.

Thứ ba, hãy phân tầng các bài test. Chạy một bộ test hồi quy 300 bài cho mỗi lần push là quá mức cần thiết. Chúng tôi sử dụng bộ “Smoke Test” (8-10 luồng quan trọng nhất) hoàn tất trong 90 giây. Chúng tôi dành bộ “Full Regression” (Hồi quy đầy đủ) cho lần triển khai cuối cùng lên production.

Kết quả

Tự động hóa với Newman đã thay đổi văn hóa của chúng tôi. Chúng tôi không còn sợ những ngày thứ Sáu nữa vì pipeline sẽ bắt được các lỗi vi phạm hợp đồng (contract breaks) trước khi chúng lên production. Các lập trình viên của chúng tôi tự tin hơn khi ship code, và đội QA đã chuyển từ việc click nút sang thiết kế các bộ test phức tạp. ROI là ngay lập tức. Nếu bạn vẫn đang phải cầu nguyện sau mỗi lần deploy, đã đến lúc để Newman đảm nhận việc xác thực.

Share: