Site logo
Tác giả
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Ngày xuất bản
Ngày xuất bản

Giới thiệu về Playwright: Công cụ Testing hiện đại cho ứng dụng Web

Playwright là gì?

Playwright là một framework automation testing mã nguồn mở được phát triển bởi Microsoft, chuyên dùng để thực hiện end-to-end testing cho các ứng dụng web. Ra mắt vào năm 2020, Playwright nhanh chóng trở thành một công cụ phổ biến trong cộng đồng phát triển phần mềm nhờ khả năng chạy test trên nhiều trình duyệt (cross-browser) và nhiều nền tảng một cách đáng tin cậy.

Không giống như các framework testing truyền thống, Playwright được thiết kế để làm việc hiệu quả với các ứng dụng web hiện đại, đặc biệt là các ứng dụng single-page (SPA), progressive web apps (PWA), và các ứng dụng web phức tạp khác. Playwright cung cấp một API mạnh mẽ, cho phép tự động hóa hầu hết mọi hành động mà người dùng có thể thực hiện trên trình duyệt web.

Những đặc điểm nổi bật của Playwright

1. Hỗ trợ đa trình duyệt (Cross-browser support)

Một trong những ưu điểm lớn nhất của Playwright là khả năng chạy các test case trên nhiều trình duyệt khác nhau một cách nhất quán. Playwright hỗ trợ:

  • Chromium: Bao gồm Google Chrome và Microsoft Edge
  • Firefox
  • WebKit: Sử dụng trong Safari

Điều này cho phép các developer và QA engineer đảm bảo rằng ứng dụng của họ hoạt động đồng nhất trên các trình duyệt phổ biến mà không cần phải viết các bộ test riêng biệt.

2. Hỗ trợ đa ngôn ngữ lập trình

Playwright hỗ trợ nhiều ngôn ngữ lập trình phổ biến, giúp các team có thể sử dụng ngôn ngữ phù hợp với stack công nghệ của họ:

  • JavaScript
  • TypeScript
  • Python
  • Java
  • C#/.NET

Sự linh hoạt này làm cho Playwright dễ dàng tích hợp vào các dự án hiện có, bất kể ngôn ngữ lập trình nào đang được sử dụng.

3. Auto-waiting và tính năng chống flakiness

Playwright được thiết kế để giảm thiểu các test case không ổn định (flaky tests) - một vấn đề phổ biến trong automated testing. Framework này:

  • Tự động đợi các element trở nên sẵn sàng trước khi tương tác
  • Đợi các animation hoàn thành
  • Đợi network requests hoàn tất
  • Đợi các thay đổi DOM

Nhờ vậy, các test case ít bị ảnh hưởng bởi vấn đề timing, tốc độ mạng hay hiệu suất máy tính.

4. Powerful API và khả năng mô phỏng người dùng

Playwright cung cấp một API mạnh mẽ cho phép mô phỏng hầu hết mọi tương tác của người dùng:

  • Click, hover, và tap
  • Điền form và tương tác với input fields
  • Nhấn phím và kết hợp phím
  • Tùy chọn upload file
  • Tương tác với dialog và alert
  • Và nhiều tính năng khác

5. Debug và Tracing

Playwright cung cấp các công cụ debug mạnh mẽ:

  • Trace Viewer: Ghi lại toàn bộ quá trình thực thi test với screenshots, DOM snapshots và network logs
  • Inspector Mode: Cho phép kiểm tra các element và thực hiện các bước test một cách tương tác
  • Debug logs: Chi tiết về các hành động được thực hiện và các lỗi xảy ra

So sánh Playwright với các framework testing khác

Tính năng Playwright Selenium Cypress Puppeteer
Hỗ trợ trình duyệt Chromium, Firefox, WebKit Chrome, Firefox, Safari, Edge, IE Chrome, Firefox, Edge Chủ yếu Chromium
Ngôn ngữ lập trình JavaScript, TypeScript, Python, Java, C# Nhiều ngôn ngữ JavaScript, TypeScript JavaScript, TypeScript
Kiến trúc Modern, sử dụng DevTools Protocol WebDriver protocol Chạy trong browser DevTools Protocol
Auto-waiting Không (cần explicit waits) Hạn chế
Parallel testing Giới hạn trong Cypress Cloud Cần cấu hình thêm
Hỗ trợ iframes Tốt Phức tạp Hạn chế Hạn chế
Mobile testing Mô phỏng Appium Giới hạn Không
Community & Support Đang phát triển Rất lớn Lớn Trung bình

Cài đặt và thiết lập Playwright

Cài đặt Playwright

Để bắt đầu với Playwright, bạn cần cài đặt nó thông qua npm (Node Package Manager). Dưới đây là các bước cơ bản:

# Tạo một project mới
mkdir playwright-demo
cd playwright-demo
npm init -y

# Cài đặt Playwright
npm init playwright@latest

Khi chạy lệnh cài đặt, Playwright sẽ hỏi một số câu hỏi cấu hình như:

  • Bạn muốn sử dụng JavaScript hay TypeScript
  • Thư mục test sẽ nằm ở đâu
  • Bạn có muốn thêm GitHub Actions workflow không
  • Bạn muốn cài đặt trình duyệt nào

Sau khi hoàn tất, Playwright sẽ tạo cấu trúc project ban đầu, cài đặt các trình duyệt cần thiết, và thêm các test ví dụ.

Cấu trúc project

Một project Playwright điển hình có cấu trúc thư mục như sau:

playwright-demo/
├── package.json
├── playwright.config.ts  // Hoặc .js tùy vào lựa chọn ngôn ngữ
├── tests/
│   ├── example.spec.ts   // Các file test
│   └── ...
└── node_modules/

File playwright.config.ts chứa các cấu hình cho project của bạn, bao gồm:

  • Trình duyệt sẽ sử dụng cho testing
  • Timeout settings
  • Cấu hình parallel testing
  • Các tùy chọn khác như screenshots, video recording, etc.

Viết test case đầu tiên với Playwright

Hãy tạo một test case đơn giản để kiểm tra việc đăng nhập vào một website:

// tests/login.spec.ts
import { test, expect } from '@playwright/test';

test('Successful login', async ({ page }) => {
  // Điều hướng đến trang đăng nhập
  await page.goto('https://example.com/login');
  
  // Điền thông tin đăng nhập
  await page.fill('input[name="email"]', 'user@example.com');
  await page.fill('input[name="password"]', 'password123');
  
  // Click vào nút đăng nhập
  await page.click('button[type="submit"]');
  
  // Kiểm tra xem đã đăng nhập thành công chưa
  // bằng cách xác minh sự hiện diện của một element chỉ xuất hiện sau khi đăng nhập
  await expect(page.locator('.dashboard-welcome')).toBeVisible();
  
  // Kiểm tra URL sau khi đăng nhập
  expect(page.url()).toContain('/dashboard');
});

Giải thích code:

  1. Import cần thiết: Import các module testexpect từ Playwright
  2. Tạo test case: Sử dụng hàm test và đặt tên cho test case
  3. Page fixture: Playwright tự động cung cấp một page object để tương tác với trình duyệt
  4. Navigation: Điều hướng đến URL cần test
  5. Tương tác: Điền form và nhấn nút submit
  6. Assertions: Kiểm tra kết quả mong đợi
    • expect(): API để kiểm tra các điều kiện
    • toBeVisible(): Kiểm tra element có hiển thị không
    • toContain(): Kiểm tra string có chứa substring

Các kỹ thuật chọn element trong Playwright

Playwright cung cấp nhiều cách để chọn các element trên trang web:

1. CSS Selectors

// Sử dụng CSS selector
await page.click('button.login-button');
await page.fill('input#username', 'testuser');

2. Text content

// Tìm element bằng text
await page.click('text=Login');
await page.click('button:has-text("Submit")');

3. Locator API (Khuyến nghị)

// Sử dụng Locator API
const loginButton = page.locator('button[type="submit"]');
await loginButton.click();

// Kết hợp nhiều điều kiện
const submitButton = page.locator('button').filter({ hasText: 'Submit' });
await submitButton.click();

4. XPath (Nếu cần thiết)

// Sử dụng XPath
await page.click('xpath=//button[contains(text(), "Login")]');

Kiểm tra các element trên trang

Playwright cung cấp nhiều phương thức để kiểm tra trạng thái của các element:

// Kiểm tra một element có visible không
await expect(page.locator('.success-message')).toBeVisible();

// Kiểm tra text content
await expect(page.locator('.greeting')).toHaveText('Welcome back, User!');

// Kiểm tra giá trị của input
await expect(page.locator('input[name="email"]')).toHaveValue('user@example.com');

// Kiểm tra thuộc tính của element
await expect(page.locator('img.avatar')).toHaveAttribute('alt', 'User profile picture');

// Kiểm tra element có bị disabled không
await expect(page.locator('button[type="submit"]')).not.toBeDisabled();

// Kiểm tra element có tồn tại trong DOM
await expect(page.locator('.error-message')).toBeAttached();

Xử lý các tình huống phổ biến trong testing

1. Xử lý Dialog và Alerts

// Đăng ký event listener trước khi dialog xuất hiện
page.on('dialog', dialog => {
  console.log(`Dialog message: ${dialog.message()}`);
  dialog.accept(); // hoặc dialog.dismiss()
});

// Thực hiện hành động gây ra dialog
await page.click('#show-dialog-button');

2. Xử lý các request network

// Chặn (mock) một API request
await page.route('**/api/users', route => {
  route.fulfill({
    status: 200,
    body: JSON.stringify([{ id: 1, name: 'Mocked User' }])
  });
});

// Đợi cho một request hoàn thành
const responsePromise = page.waitForResponse('**/api/login');
await page.click('#login-button');
const response = await responsePromise;
expect(response.status()).toBe(200);

3. Upload file

// Upload một file
await page.setInputFiles('input[type="file"]', 'path/to/file.jpg');

// Upload nhiều file
await page.setInputFiles('input[type="file"]', ['file1.jpg', 'file2.jpg']);

4. Xử lý iframes

// Tương tác với element trong iframe
const frame = page.frameLocator('#my-iframe');
await frame.locator('button.submit').click();

5. Mobile testing và responsive design

// Cấu hình chế độ mobile
await page.setViewportSize({ width: 375, height: 812 }); // iPhone X

// Mô phỏng device cụ thể
await page.emulate({
  userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15A372 Safari/605.1.15',
  viewport: { width: 375, height: 812 },
  deviceScaleFactor: 3,
  isMobile: true,
  hasTouch: true
});

Các kỹ thuật nâng cao trong Playwright

1. Parallel testing

Playwright cho phép chạy các test parallel để tiết kiệm thời gian. Cấu hình trong playwright.config.ts:

export default defineConfig({
  // Số lượng worker để chạy các test song song
  workers: 5,
  // Hoặc sử dụng % số CPU
  // workers: '50%',
});

2. Test grouping và tagging

import { test } from '@playwright/test';

// Grouping các test liên quan
test.describe('Authentication flows', () => {
  test('User can login', async ({ page }) => {
    // test login
  });
  
  test('User can logout', async ({ page }) => {
    // test logout
  });
});

// Tagging
test('User can purchase a product @smoke @e2e', async ({ page }) => {
  // test purchase flow
});

// Chạy test với tag cụ thể
// npx playwright test --grep @smoke

3. Testing with authentication

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

// Định nghĩa fixture cho authenticated state
const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    // Login logic
    await page.goto('/login');
    await page.fill('[name=email]', 'user@example.com');
    await page.fill('[name=password]', 'password123');
    await page.click('button[type=submit]');
    await page.waitForURL('/dashboard');
    
    // Chuyển page đã đăng nhập cho test sử dụng
    await use(page);
  }
});

// Sử dụng authenticated fixture
test('User can view profile', async ({ authenticatedPage }) => {
  await authenticatedPage.click('text=My Profile');
  await expect(authenticatedPage.locator('h1')).toHaveText('User Profile');
});

4. Visual testing với Screenshot comparison

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

test('homepage looks correct', async ({ page }) => {
  await page.goto('https://example.com');
  
  // So sánh toàn bộ trang với screenshot chuẩn
  await expect(page).toHaveScreenshot('homepage.png', {
    maxDiffPixelRatio: 0.01 // Cho phép sai khác 1%
  });
  
  // So sánh chỉ một vùng cụ thể
  await expect(page.locator('.navbar')).toHaveScreenshot('navbar.png');
});

Best Practices khi sử dụng Playwright

1. Tổ chức test code

  • Page Object Model (POM): Tạo các class cho mỗi page để encapsulate các selector và logic
  • Fixture sử dụng lại: Tạo các fixture cho những tác vụ thường xuyên thực hiện
  • Tách biệt test data: Đặt test data trong các file riêng biệt

Ví dụ về Page Object Model:

// models/LoginPage.ts
export class LoginPage {
  constructor(private page) {}
  
  async navigate() {
    await this.page.goto('/login');
  }
  
  async login(email: string, password: string) {
    await this.page.fill('[name=email]', email);
    await this.page.fill('[name=password]', password);
    await this.page.click('button[type=submit]');
  }
  
  async getErrorMessage() {
    return this.page.locator('.error-message').textContent();
  }
}

// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../models/LoginPage';

test('User can login with valid credentials', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.navigate();
  await loginPage.login('valid@example.com', 'correctpassword');
  await expect(page).toHaveURL('/dashboard');
});

2. Giữ test độc lập

  • Mỗi test case nên hoàn toàn độc lập, không phụ thuộc vào các test case khác
  • Sử dụng test.beforeEach() để thiết lập trạng thái ban đầu
  • Cleanup sau mỗi test bằng test.afterEach()

3. Tối ưu hiệu suất test

  • Sử dụng authentication state storage thay vì đăng nhập lại mỗi test
  • Sử dụng mock API cho những request không cần thiết
  • Chọn selector tối ưu, ưu tiên data-testid hoặc các attribute có mục đích test

Tích hợp Playwright vào CI/CD pipeline

Playwright có thể dễ dàng tích hợp vào các hệ thống CI/CD phổ biến:

Tài liệu tham khảo và cộng đồng

Tổng kết

Playwright đang nhanh chóng trở thành một công cụ automation testing ưa thích của nhiều developer và QA engineer nhờ khả năng cross-browser testing đáng tin cậy, API mạnh mẽ và dễ sử dụng, cùng nhiều tính năng hiện đại giúp giảm thiểu "flaky tests".

Với khả năng hoạt động trên nhiều trình duyệt và hỗ trợ nhiều ngôn ngữ lập trình, Playwright phù hợp với nhiều dự án khác nhau, từ các ứng dụng web nhỏ đến các hệ thống phức tạp với nhiều thành phần.

Trong bối cảnh các ứng dụng web ngày càng phức tạp và yêu cầu về chất lượng ngày càng cao, Playwright cung cấp một giải pháp testing hiệu quả, giúp các team phát hiện và khắc phục các vấn đề sớm, từ đó cải thiện trải nghiệm người dùng và giảm chi phí khắc phục lỗi trong các giai đoạn sau của dự án.

Hãy bắt đầu với Playwright ngay hôm nay và khám phá cách nó có thể cải thiện quy trình testing của bạn!