Viết E2E test cho Angular bằng Cypress

Lý do viết E2E test

  • E2E (End-to-End) tests giúp kiểm tra toàn bộ luồng ứng dụng như người dùng thật.
  • Cypress là lựa chọn nhanh, mạnh và dễ dùng hơn Protractor, đặc biệt với Angular và môi trường NX workspace.

🛠️ 1. Thiết lập Cypress và viết test đầu tiên

✅ Các bước chính:

  1. Cài Cypress bằng:
    npm install --save-dev @nx/cypress
  2. Tạo dự án E2E:
    nx g @nx/cypress:cypress-project ng-cypress-starter-e2e --project=ng-cypress-starter
  3. Chạy test mặc định:
    npm run e2e ng-cypress-starter

🧪 Viết test đầu tiên:

  • Mục tiêu: Kiểm tra tiêu đề trang có chứa "Your first Cypress test in Angular"
// src/support/app.po.ts
export const getHeaderTitle = () => cy.get('.toolbar__title');

// src/e2e/app.cy.ts
describe('ng-cypress-starter', () => {
beforeEach(() => cy.visit('/'));

it('should display the correct header title', () => {
getHeaderTitle().should('contain.text','Your first Cypress test in Angular');
});
});

👁️ 2. Kiểm tra visibility của phần tử

🎯 Mục tiêu:

  • Kiểm tra ẩn/hiện của component dựa trên nút toggle.
  • Kiểm tra hiển thị nút hành động khi hover.

✍️ Một số selectors:

export const getToggleCounterButton = () => cy.get('[data-test-id="toggleCounterBtn"]');
export const getCounterCard = () => cy.get('[data-test-id="counterCard"]');
export const getCounterActions = () => getCounterCard().find('button');

📌 Vấn đề:

  • cy.trigger('mouseover') không đủ để CSS pseudo :hover hoạt động.

✅ Giải pháp:

  • Cài thêm cypress-real-events:
npm install --save-dev cypress-real-events
  • Sử dụng .realHover():
getCounterCard().realHover();
getCounterActions().should('have.length', 3);

📝 3. Kiểm tra form và validation

🧪 Mục tiêu test:

  • Nút Submit bị disable khi input không hợp lệ.
  • Hiện log mới khi submit thành công.
  • Hiện lỗi khi input rỗng hoặc sai định dạng.

💡 Cách viết assertions:

getSubmitButton().should('be.disabled');
getRequiredError().should('be.visible');
getMismatchError().should('contain.text', 'Version number does not match...');

🔍 Ghi chú:

  • Dùng .trim() để so sánh text chính xác, tránh lỗi do khoảng trắng.

🔁 4. Chờ XHR hoàn tất trước khi kiểm tra

🧪 Vấn đề:

  • HTTP call bị delay 5000ms, trong khi Cypress chỉ chờ 4000ms mặc định.

✅ Giải pháp:

  • Sử dụng intercept + alias + wait:
cy.intercept('/assets/users.json').as('searchUsers');
cy.wait('@searchUsers', { timeout: 10000 });

🔧 5. Sử dụng các thư viện kèm sẵn của Cypress

💼 Bundled tools:

  • jQuery: Cypress.$
  • Lodash: Cypress._
  • Minimatch: Cypress.minimatch

🧪 Ví dụ kiểm tra:

// Đảm bảo tên người dùng là duy nhất
const names = _.map(cards, card => $(card).find('h4').text());
const uniqueNames = _.uniq(names);
expect(names.length).to.equal(uniqueNames.length);
  • So khớp URL có query params động:
minimatch(url, `${location.origin}/users/${uuid}*`)

📁 6. Dùng Fixtures để mô phỏng dữ liệu

🎯 Mục tiêu:

  • Tránh gọi API thật → sử dụng dữ liệu giả lập (fixture) cho HTTP call.

📌 Cách dùng:

cy.fixture('get-bucket').then((resp) => {
cy.intercept('GET', 'http://localhost:3333/api/bucket', resp);
});

📦 Fixtures dùng trong ví dụ:

  • get-bucket.json: dữ liệu ban đầu
  • add-bucket-item.json: phản hồi khi thêm
  • delete-bucket-item.json: phản hồi khi xoá

🎯 Ghi chú tổng hợp

Kỹ thuậtCông cụ / LệnhGhi chú
Viết test.should()Kiểm tra DOM, text, class, focus…
Hover CSSrealHover()Cần cypress-real-events
Wait XHRcy.intercept() + cy.wait()Đảm bảo test chờ đúng thời điểm
Fixturescy.fixture() + cy.intercept()Tránh gọi thật đến API
Debug DOMCypress.$Dùng jQuery để log, kiểm tra
Assert dataexpect()Với .then(el => expect(...))

📚 Tài nguyên tham khảo

Để lại một bình luận