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.
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:
-
/logincó trả về token không? -
/userscó trả về đúng định dạng không? -
POST /postscó 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:
- Nhanh hơn UI flow: không render giao diện, không chờ element.
-
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)
Ví dụ GET đơn giản:
cy.request('https://jsonplaceholder.typicode.com/users')
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
}
})
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: đặtfalsenếu muốn tự assert response4xxhoặc5xx.
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)
})
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ếuContent-Typelà 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')
})
})
})
Vì 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')
Assert response headers:
cy.request('https://jsonplaceholder.typicode.com/users').then((response) => {
expect(response.headers['content-type']).to.include('application/json')
})
Để đà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 API và cá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:
- Gửi request đăng nhập.
- Lấy token từ response.
- 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')
})
})
})
})
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)
})
})
})
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' }]
})
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')
})
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 API và API 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
Ý 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. -
-dhoặ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/CD và 30 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)
})
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() và 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)