Viết Test case với Playwright

Cách viết Test Case hiệu quả với Playwright: Từ Test Design đến Automation Script

Playwright là gì? Tại sao cần viết test case đúng cách?

Playwright là framework automation testing mã nguồn mở do Microsoft phát triển, hỗ trợ kiểm thử trên nhiều trình duyệt (Chromium, Firefox, WebKit) với một codebase duy nhất. Không chỉ mạnh mẽ về kỹ thuật, Playwright còn cung cấp API trực quan giúp tester và developer viết test case nhanh, ổn định và dễ bảo trì.

Tuy nhiên, viết test case với Playwright không chỉ đơn thuần là “ghi lại thao tác rồi chạy”. Một test case hiệu quả đòi hỏi tư duy Test Design rõ ràng, xác định đúng điều cần kiểm tra, trước khi chuyển sang viết Automation Script.

Viet test case bang AI (3)

Test Design là gì? Tại sao phải làm trước khi code?

Test Design là quá trình xác định:

  • Mục tiêu kiểm thử (What to test?)
  • Điều kiện đầu vào (Test data, preconditions)
  • Kết quả mong đợi (Expected outcomes)
  • Các bước thực hiện (Steps to reproduce)

Bỏ qua bước này dẫn đến các test case bị trùng lặp, thiếu coverage, khó debug khi fail, và quan trọng hơn là không phản ánh đúng yêu cầu nghiệp vụ.

Cấu trúc một Test Case chuẩn với Playwright

1. Đặt tên test có ý nghĩa

typescript
// ❌ Không rõ ràng
test('test login', async ({ page }) => { ... });

// ✅ Rõ ràng, đọc như tài liệu
test('should display error message when login with wrong password', async ({ page }) => { ... });

Nguyên tắc đặt tên: [hành vi mong đợi] + [ngữ cảnh/điều kiện]

2. Áp dụng mô hình AAA: Arrange, Act, Assert

Đây là cấu trúc nền tảng của mọi test case tốt:

typescript
test('should add item to cart successfully', async ({ page }) => {
  // ARRANGE — Chuẩn bị dữ liệu và trạng thái ban đầu
  await page.goto('/products');
  await page.waitForSelector('.product-list');

  // ACT — Thực hiện hành động cần kiểm thử
  await page.click('[data-testid="add-to-cart-btn"]');

  // ASSERT — Kiểm tra kết quả
  await expect(page.locator('.cart-count')).toHaveText('1');
});

3. Sử dụng Locator Strategy đúng cách

Playwright khuyến khích sử dụng user-facing locators thay vì CSS/XPath phức tạp:

typescript
// ❌ Dễ vỡ khi UI thay đổi
await page.click('#app > div.main > button:nth-child(3)');

// ✅ Bền vững, gần với cách người dùng tương tác
await page.getByRole('button', { name: 'Đăng nhập' }).click();
await page.getByLabel('Email').fill('user@example.com');
await page.getByTestId('submit-btn').click();

Thứ tự ưu tiên locator trong Playwright:

  1. getByRole(): Tốt nhất, theo ARIA role
  2. getByLabel(): Cho form inputs
  3. getByText(): Cho text content
  4. getByTestId(): Khi cần selector ổn định
  5. locator() với CSS/XPath: Cuối cùng mới dùng

4. Tổ chức test với describebeforeEach

typescript
import { test, expect } from '@playwright/test';

test.describe('Tính năng Đăng nhập', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/login');
  });

  test('đăng nhập thành công với thông tin hợp lệ', async ({ page }) => {
    await page.getByLabel('Email').fill('admin@test.com');
    await page.getByLabel('Mật khẩu').fill('Admin@123');
    await page.getByRole('button', { name: 'Đăng nhập' }).click();

    await expect(page).toHaveURL('/dashboard');
  });

  test('hiển thị lỗi khi mật khẩu sai', async ({ page }) => {
    await page.getByLabel('Email').fill('admin@test.com');
    await page.getByLabel('Mật khẩu').fill('wrong-password');
    await page.getByRole('button', { name: 'Đăng nhập' }).click();

    await expect(page.getByRole('alert')).toContainText('Sai tên đăng nhập hoặc mật khẩu');
  });
});

5. Data-Driven Testing: Test nhiều bộ dữ liệu

typescript
const loginTestCases = [
  { email: '', password: 'valid', expected: 'Email không được để trống' },
  { email: 'invalid-email', password: 'valid', expected: 'Email không hợp lệ' },
  { email: 'user@test.com', password: '', expected: 'Mật khẩu không được để trống' },
];

for (const tc of loginTestCases) {
  test(`validation: ${tc.expected}`, async ({ page }) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill(tc.email);
    await page.getByLabel('Mật khẩu').fill(tc.password);
    await page.getByRole('button', { name: 'Đăng nhập' }).click();

    await expect(page.getByRole('alert')).toContainText(tc.expected);
  });
}

6. Page Object Model (POM): Tái sử dụng và bảo trì dễ dàng

Khi dự án lớn lên, Page Object Model là pattern không thể thiếu:

typescript
// pages/LoginPage.ts
export class LoginPage {
  constructor(private page: Page) {}

  async goto() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.page.getByLabel('Email').fill(email);
    await this.page.getByLabel('Mật khẩu').fill(password);
    await this.page.getByRole('button', { name: 'Đăng nhập' }).click();
  }

  async getErrorMessage() {
    return this.page.getByRole('alert').textContent();
  }
}

// tests/login.spec.ts
test('đăng nhập thành công', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login('admin@test.com', 'Admin@123');
  await expect(page).toHaveURL('/dashboard');
});
Cách viết testcase với Playwright

Các lỗi phổ biến khi viết test case với Playwright

LỗiHậu quảCách khắc phục
Dùng page.waitForTimeout(3000)Test chậm, không ổn địnhDùng waitForSelector, expect().toBeVisible()
Không có data-testidLocator bị vỡ khi refactor UIThống nhất với developer đặt test ID
Test phụ thuộc vào thứ tự chạyFlaky testsMỗi test phải độc lập, tự chuẩn bị data
Assert quá nhiều trong 1 testKhó debug khi fail1 test — 1 assertion chính
Bỏ qua negative casesThiếu coverageLuôn viết test cho edge cases và lỗi

Thêm một đoạn văn bản ở đây. Nhấp vào ô văn bản để tùy chỉnh nội dung, phong cách phông chữ và màu sắc của đoạn văn của bạn.

Checklist viết Test Case với Playwright

Trước khi commit test, kiểm tra các tiêu chí sau:

  • Tên test mô tả rõ hành vi cần kiểm tra
  • Test có cấu trúc Arrange – Act – Assert
  • Không dùng waitForTimeout cứng
  • Locator theo getByRole, getByLabel, hoặc getByTestId
  • Test chạy độc lập, không phụ thuộc test khác
  • Có cả positive case và negative case
  • Dữ liệu test không hardcode trong production logic
  • Test pass ổn định sau 3 lần chạy liên tiếp

Tổng kết: Các bước phải làm để viết test case với Playwright

1. Phân tích requirement → Xác định Test Condition
2. Thiết kế Test Case → Đặt tên, input, expected output
3. Chọn Locator Strategy → Ưu tiên user-facing selectors
4. Viết script theo AAA → Arrange, Act, Assert
5. Áp dụng POM → Tái sử dụng, dễ maintain
6. Review & Refactor → Đảm bảo stability và readability

🎯 Bạn muốn thực hành Playwright từ cơ bản đến nâng cao?

Khóa học Playwright chuyên sâu của CO-WELL sẽ giúp bạn:

✅ Nắm vững Test Design Thinking — biết cái gì cần test trước khi code
✅ Viết Automation Script chuẩn với TypeScript + Playwright
✅ Áp dụng Page Object Model và Data-Driven Testing thực chiến
✅ Tích hợp test vào CI/CD pipeline
✅ Xử lý các tình huống phức tạp: iFrame, Upload/Download, API Mocking

✅ Sau khóa học có project thực tế để đưa vào CV và luyện tập phỏng vấn theo kỹ thuật STAR.

🚀 Khai giảng: 27/5/2025
Số lượng học viên có giới hạn — Đăng ký sớm để đảm bảo suất học!

👉 [Đăng ký ngay Khóa Playwright CO-WELL →]

Các tin tức khác