DEV Community

Cover image for Kiểm thử API Cypress: Cách dùng cy.request() để test API
Sebastian Petrus
Sebastian Petrus

Posted on • Originally published at apidog.com

Kiểm thử API Cypress: Cách dùng cy.request() để test API

Bạn đã chạy Cypress cho các bài kiểm thử end-to-end. Bây giờ bạn muốn gọi trực tiếp API, kiểm tra mã trạng thái và xác nhận JSON mà không cần đi qua UI. Cypress hỗ trợ việc này bằng cy.request(): lệnh gửi HTTP request thực từ Node, bên ngoài trình duyệt, phù hợp để kiểm thử endpoint riêng lẻ hoặc thiết lập trạng thái trước khi chạy UI test.

Dùng thử Apidog ngay hôm nay

Bài viết này hướng dẫn cách kiểm thử API với Cypress: dùng cy.request(), xác nhận response, xâu chuỗi request để tái sử dụng token xác thực, và dùng cy.intercept() để stub request từ trình duyệt. Bạn cũng sẽ thấy khi nào Cypress đủ tốt cho API test, và khi nào nên dùng công cụ chuyên biệt cho API.

Tại sao nên kiểm thử API với Cypress

Phần lớn Cypress test điều khiển trình duyệt, nhưng nhiều hành vi UI phụ thuộc vào API bên dưới:

  • /login có trả về token không?
  • /users có trả về đúng định dạng không?
  • POST /posts có tạo dữ liệu như mong đợi không?

Kiểm thử trực tiếp endpoint có hai lợi ích chính:

  1. Nhanh hơn UI flow: không render giao diện, không chờ element.
  2. Dễ setup dữ liệu: thay vì click qua form đăng ký, bạn gửi POST để tạo user rồi bắt đầu assertion chính.

Nếu Cypress đã có trong stack của bạn, thêm API test cũng giúp bạn dùng cùng test runner, cùng assertion expect, cùng CI pipeline.

Cơ bản về cy.request()

cy.request() tạo HTTP request và trả về response. Lệnh này chạy từ tiến trình Node của Cypress, không phải từ trình duyệt, nên không bị ảnh hưởng bởi CORS hoặc same-origin policy.

Các dạng thường dùng:

cy.request(url)
cy.request(url, body)
cy.request(method, url)
cy.request(method, url, body)
cy.request(options)
Enter fullscreen mode Exit fullscreen mode

Ví dụ GET đơn giản:

cy.request('https://jsonplaceholder.typicode.com/users')
Enter fullscreen mode Exit fullscreen mode

Với request cần method, body hoặc header, dùng object options:

cy.request({
  method: 'POST',
  url: 'https://jsonplaceholder.typicode.com/posts',
  headers: {
    'Content-Type': 'application/json'
  },
  body: {
    title: 'API test',
    body: 'created from Cypress',
    userId: 1
  }
})
Enter fullscreen mode Exit fullscreen mode

Các option hữu ích:

  • method: HTTP method như GET, POST, PUT, PATCH, DELETE. Mặc định là GET.
  • url: endpoint cần gọi.
  • body: payload request. Cypress tự chuyển object thành JSON.
  • headers: request headers.
  • auth: thông tin cho HTTP Basic Authentication.
  • qs: query string dưới dạng object.
  • failOnStatusCode: đặt false nếu muốn tự assert response 4xx hoặc 5xx.

Ví dụ test negative case với 404:

cy.request({
  method: 'GET',
  url: 'https://jsonplaceholder.typicode.com/users/99999',
  failOnStatusCode: false
}).then((response) => {
  expect(response.status).to.eq(404)
})
Enter fullscreen mode Exit fullscreen mode

Nếu không đặt failOnStatusCode: false, Cypress sẽ fail test khi response không phải 2xx hoặc 3xx.

Xác nhận status, body và headers

cy.request() trả về response object. Bạn đọc response bằng .then().

Các field thường dùng:

  • status: HTTP status code.
  • body: response body. Nếu Content-Type là JSON, Cypress parse thành JavaScript object.
  • headers: response headers.
  • duration: thời gian request tính bằng millisecond.

Ví dụ test hoàn chỉnh:

describe('Users API', () => {
  it('returns a list of users', () => {
    cy.request('https://jsonplaceholder.typicode.com/users').then((response) => {
      expect(response.status).to.eq(200)
      expect(response.body).to.be.an('array')
      expect(response.body).to.have.length(10)
      expect(response.body[0]).to.have.property('email')
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

response.body đã được parse, bạn có thể assert bằng Chai như trong UI test:

expect(response.body).to.be.an('array')
expect(response.body[0]).to.have.property('email')
expect(response.body[0].address).to.have.property('city')
Enter fullscreen mode Exit fullscreen mode

Assert response headers:

cy.request('https://jsonplaceholder.typicode.com/users').then((response) => {
  expect(response.headers['content-type']).to.include('application/json')
})
Enter fullscreen mode Exit fullscreen mode

Để đào sâu hơn về cách viết assertion cho API, xem thêm hướng dẫn về xác nhận APIcác phương pháp hay nhất về kiểm thử API.

Xâu chuỗi request và tái sử dụng token xác thực

API thực tế thường cần xác thực. Pattern phổ biến:

  1. Gửi request đăng nhập.
  2. Lấy token từ response.
  3. Gửi token trong request tiếp theo.

Ví dụ:

describe('Authenticated API flow', () => {
  it('logs in and fetches a protected resource', () => {
    cy.request({
      method: 'POST',
      url: 'https://api.example.com/login',
      body: {
        email: 'user@example.com',
        password: 'secret'
      }
    }).then((loginResponse) => {
      expect(loginResponse.status).to.eq(200)

      const token = loginResponse.body.token

      cy.request({
        method: 'GET',
        url: 'https://api.example.com/profile',
        headers: {
          Authorization: `Bearer ${token}`
        }
      }).then((profileResponse) => {
        expect(profileResponse.status).to.eq(200)
        expect(profileResponse.body).to.have.property('email', 'user@example.com')
      })
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

Nếu nhiều test cần cùng token, đưa bước đăng nhập vào beforeEach() và lưu token bằng alias hoặc Cypress.env().

Ví dụ dùng alias:

describe('Authenticated API tests', () => {
  beforeEach(() => {
    cy.request({
      method: 'POST',
      url: 'https://api.example.com/login',
      body: {
        email: 'user@example.com',
        password: 'secret'
      }
    }).then((response) => {
      cy.wrap(response.body.token).as('token')
    })
  })

  it('fetches profile', function () {
    cy.request({
      method: 'GET',
      url: 'https://api.example.com/profile',
      headers: {
        Authorization: `Bearer ${this.token}`
      }
    }).then((response) => {
      expect(response.status).to.eq(200)
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

Pattern đăng nhập rồi dùng token cũng giống các luồng kiểm thử tích hợp API, nơi response của request trước cung cấp dữ liệu cho request sau.

Dùng cy.intercept() để stub request từ trình duyệt

cy.request() gọi API thật. Ngược lại, cy.intercept() dùng để chặn request mà ứng dụng front-end gửi từ trình duyệt và trả về response giả lập.

Điểm cần nhớ:

  • cy.request() không bị cy.intercept() chặn vì nó chạy bên ngoài trình duyệt.
  • cy.intercept() chỉ áp dụng cho request do app front-end tạo ra trong browser.
  • Dùng cy.intercept() khi bạn muốn kiểm thử UI phản ứng thế nào với một API response cụ thể.

Stub một response tĩnh:

cy.intercept('GET', '/api/users', {
  statusCode: 200,
  body: [{ id: 1, name: 'Ada' }]
})
Enter fullscreen mode Exit fullscreen mode

Từ thời điểm này, request trình duyệt đến /api/users sẽ nhận payload cố định đó.

Bạn có thể dùng cách này để test các case khó tạo với backend thật:

  • danh sách rỗng
  • lỗi 500
  • response chậm
  • dữ liệu thiếu field
  • trạng thái loading

Ví dụ test UI khi API lỗi:

it('shows an error banner when the API fails', () => {
  cy.intercept('GET', '/api/users', {
    statusCode: 500,
    body: { message: 'Server error' }
  }).as('getUsers')

  cy.visit('/dashboard')

  cy.wait('@getUsers')
    .its('response.statusCode')
    .should('eq', 500)

  cy.contains('Something went wrong').should('be.visible')
})
Enter fullscreen mode Exit fullscreen mode

Luôn đặt cy.intercept() trước hành động kích hoạt request. Nếu đặt sau cy.visit() hoặc sau click event, Cypress có thể bỏ lỡ request.

Nếu bạn đang cân nhắc khi nào nên stub một response và khi nào nên mock một service, xem thêm bài viết về giả lập APIAPI stubbing so với API mocking.

Khi nào nên dùng Cypress cho API test

Cypress phù hợp khi API test hỗ trợ trực tiếp cho UI test:

  • seed dữ liệu trước khi chạy UI flow
  • xác minh endpoint mà UI phụ thuộc vào
  • login bằng API để bỏ qua form đăng nhập
  • stub response để test trạng thái lỗi hoặc loading
  • viết một vài smoke test API trong cùng test suite

Cách này giúp bạn giữ mọi thứ trong cùng Cypress project và cùng CI run.

Khi nào Cypress không phải lựa chọn tốt nhất

Cypress trở nên kém phù hợp hơn khi bạn có một bộ API test độc lập và lớn.

Một số giới hạn thường gặp:

  • Không có request builder trực quan: mọi request đều là code. Khi có hàng trăm endpoint, viết thủ công header, body và query param sẽ chậm.
  • Không tối ưu cho API-only CI pipeline: Cypress được xây dựng cho browser testing, nên API-only run vẫn mang theo overhead không cần thiết.
  • Không có định nghĩa API dùng chung: request nằm trong test file thay vì một đặc tả API mà team có thể tái sử dụng cho thiết kế, tài liệu và kiểm thử.

Đây là lúc công cụ chuyên biệt cho API phù hợp hơn. Apidog được xây dựng xoay quanh API: bạn thiết kế endpoint, xây dựng test scenario từ đó, dùng request builder trực quan và assertion trực quan cho các case phổ biến. Request, environment và authentication nằm trong một workspace chung để team cùng sử dụng.

Với CI, Apidog CLI có thể chạy các test scenario đã lưu trong môi trường headless:

npm install -g apidog-cli

apidog run \
  --access-token "$APIDOG_ACCESS_TOKEN" \
  -t <scenarioOrSuiteId> \
  -e <environmentId> \
  -r cli,html,junit
Enter fullscreen mode Exit fullscreen mode

Ý nghĩa các flag:

  • -t: chọn scenario hoặc suite đã lưu.
  • -e: chọn environment.
  • -r: chọn định dạng report như cli, html, json, junit.
  • -d hoặc --iteration-data: chạy test với file dữ liệu.
  • --upload-report: đẩy kết quả về workspace.

Apidog CLI chạy các scenario đã lưu; nó không phải công cụ gửi request tương tác.

Cách chọn thực tế:

  • Dùng cy.request() khi API check phục vụ browser test.
  • Dùng cy.intercept() khi cần kiểm soát response mà UI nhận được.
  • Dùng công cụ chuyên biệt cho API khi API testing là công việc chính của pipeline.

Nếu bạn đang so sánh các lựa chọn, xem thêm danh sách các công cụ tự động hóa kiểm thử API chạy trong CI/CD30 công cụ kiểm thử API tốt nhất.

Câu hỏi thường gặp

cy.request() có chạy trong trình duyệt không?

Không. cy.request() chạy từ tiến trình Node của Cypress, bên ngoài trình duyệt. Vì vậy nó không bị CORS hoặc same-origin policy chặn, và cy.intercept() không thể chặn nó.

Nếu bạn cần kiểm tra request do trình duyệt gửi, hãy kích hoạt request qua ứng dụng và bắt nó bằng cy.intercept().

Làm thế nào để test response 400 hoặc 404 mà không fail test?

Đặt failOnStatusCode: false, sau đó tự assert status:

cy.request({
  method: 'GET',
  url: '/api/users/unknown',
  failOnStatusCode: false
}).then((response) => {
  expect(response.status).to.eq(404)
})
Enter fullscreen mode Exit fullscreen mode

Có thể dùng cy.request() để đăng nhập trước UI test không?

Có. Đây là pattern phổ biến để tăng tốc test.

Bạn gửi POST đến endpoint đăng nhập, lấy token từ response.body, rồi lưu vào Cypress.env(), localStorage hoặc cookie để UI test bắt đầu trong trạng thái đã xác thực.

Khác nhau giữa cy.request()cy.intercept() là gì?

cy.request() gửi HTTP request thật và nhận response thật. Dùng nó để kiểm thử API hoặc setup dữ liệu.

cy.intercept() theo dõi hoặc giả lập request mà front-end gửi trong trình duyệt. Dùng nó để kiểm soát response mà UI nhận được.

Nên dùng Cypress hay công cụ chuyên biệt cho API testing?

Dùng Cypress khi API check hỗ trợ bộ UI test bạn đã có.

Với bộ API test độc lập lớn, API-only CI pipeline, hoặc nhu cầu dùng chung định nghĩa API trong team, công cụ chuyên biệt như Apidog sẽ phù hợp hơn. Bạn có thể tham khảo thêm các phương pháp hay nhất về kiểm thử API.

Top comments (0)