debug playwright flaky test (1)

Debug Playwright: Xử lý Flaky Test, Locator không ổn định và các Anti-pattern phổ biến

Playwright Flaky Test là gì? Tại sao nó xảy ra?

Flaky test là các test case khi chạy có thể cho kết quả pass hoặc fail không nhất quán — dù code ứng dụng không thay đổi. Đây là vấn đề cực kỳ phổ biến trong automation testing, đặc biệt khi làm việc với Playwright.

Nguyên nhân chính của flaky test trong Playwright bao gồm:

  • Race condition: Test thực thi nhanh hơn ứng dụng có thể phản hồi
  • Locator không ổn định: Sử dụng CSS selector dễ thay đổi hoặc XPath phức tạp
  • Dữ liệu test không cô lập: Các test case chia sẻ trạng thái với nhau
  • Môi trường không nhất quán: Tốc độ mạng, hiệu năng máy chủ biến động
  • Implicit wait thiếu hoặc sai: Không chờ đúng trạng thái của DOM

Câu hỏi thường gặp về Debug Playwright Flaky Test

Làm thế nào để phát hiện flaky test trong Playwright?

Playwright cung cấp flag --retries để chạy lại test thất bại. Nếu test pass ở lần thứ 2 hoặc 3, đó là dấu hiệu rõ ràng của flaky test. Bạn cũng có thể dùng --reporter=html để xem chi tiết từng bước thực thi.

npx playwright test --retries=3 --reporter=html

Playwright có công cụ debug nào không?

Có. Playwright cung cấp ba công cụ debug chính:

  1. Playwright Inspector (PWDEBUG=1): Cho phép bước qua từng action
  2. Trace Viewer: Ghi lại toàn bộ quá trình test, bao gồm screenshots, network logs
  3. VS Code Extension: Debug trực tiếp trong IDE
# Chạy với Playwright Inspector
PWDEBUG=1 npx playwright test

# Bật trace để xem lại
npx playwright test --trace on

Top 5 Anti-pattern phổ biến khi viết Playwright Test

1. Sử dụng page.waitForTimeout() Hard Wait

Đây là anti-pattern số một mà developer mới hay mắc phải.

// ❌ Sai — hard wait không đáng tin cậy
await page.waitForTimeout(3000);
await page.click('#submit-button');

// ✅ Đúng — chờ element thực sự sẵn sàng
await page.getByRole('button', { name: 'Submit' }).click();

waitForTimeout tạo ra “sleep” cứng. Nếu ứng dụng load nhanh hơn, test lãng phí thời gian. Nếu chậm hơn, test vẫn fail. Thay vào đó, hãy dùng auto-waiting của Playwright các action như click(), fill() tự động chờ element actionable.

2. Locator không ổn định: Fragile Selectors

// ❌ Sai — dễ vỡ khi UI thay đổi
await page.click('div.container > ul > li:nth-child(3) > a');
await page.click('[class="btn btn-primary active"]');

// ✅ Đúng — semantic locator bền vững
await page.getByRole('link', { name: 'Đăng nhập' }).click();
await page.getByTestId('login-button').click();
await page.getByLabel('Email').fill('test@example.com');

Thứ tự ưu tiên locator theo khuyến nghị chính thức của Playwright:

Ưu tiênLocatorLý do
1getByRole()Semantic, phản ánh cách user tương tác
2getByLabel()Gắn với form label
3getByPlaceholder()Dành cho input
4getByTestId()Stable nếu dùng data-testid
5getByText()Dùng khi không có cách nào khác
⛔ TránhCSS/XPath phức tạpDễ vỡ, khó bảo trì

3. Không cô lập Test Data – Shared State

// ❌ Sai — test phụ thuộc vào thứ tự chạy
test('test 2 assumes user is logged in', async ({ page }) => {
  // Giả định test 1 đã login — rất nguy hiểm!
  await page.goto('/dashboard');
});

// ✅ Đúng — mỗi test tự thiết lập state của mình
test.beforeEach(async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('user@test.com');
  await page.getByLabel('Password').fill('password');
  await page.getByRole('button', { name: 'Login' }).click();
  await page.waitForURL('/dashboard');
});

Hoặc tốt hơn, dùng storageState để tái sử dụng session:

// playwright.config.ts
use: {
  storageState: 'auth.json',
}

4. Không xử lý Dynamic Content – Async Race Condition

// ❌ Sai — assert ngay khi element chưa cập nhật
await page.click('#load-more');
const count = await page.locator('.item').count();
expect(count).toBe(20); // Có thể vẫn là 10!

// ✅ Đúng — dùng expect() với auto-retry
await page.click('#load-more');
await expect(page.locator('.item')).toHaveCount(20);

expect() trong Playwright tự động retry assertion cho đến khi pass hoặc hết timeout. Đây là cơ chế cực kỳ quan trọng để tránh race condition.

5. Test quá lớn: God Test Anti-pattern

// ❌ Sai — một test làm quá nhiều việc
test('everything', async ({ page }) => {
  // Login
  // Tạo user
  // Upload file  
  // Gửi email
  // Kiểm tra báo cáo
  // Logout
  // ... 200 dòng code
});

// ✅ Đúng — mỗi test kiểm tra một behavior
test('user can upload avatar successfully', async ({ page }) => {
  // Chỉ tập trung vào upload avatar
});

Hướng dẫn Debug Playwright Flaky Test từng bước

Bước 1: Tái hiện lỗi có hệ thống

# Chạy test cụ thể nhiều lần để xác nhận flakiness
npx playwright test tests/login.spec.ts --repeat-each=10

Bước 2: Bật Trace Viewer để điều tra

npx playwright test --trace on
npx playwright show-report

Trace Viewer cho bạn thấy:

  • Screenshot tại từng action
  • Network requests & responses
  • Console logs và errors
  • Thời gian thực thi từng bước

Bước 3: Dùng Playwright Inspector để debug live

PWDEBUG=1 npx playwright test tests/login.spec.ts

Inspector cho phép bạn:

  • Pause tại bất kỳ điểm nào
  • Inspect locator ngay trong browser
  • Step through từng action
  • Xem DOM tree theo thời gian thực

Bước 4: Kiểm tra Network và Console Errors

// Bắt lỗi console trong test
page.on('console', msg => {
  if (msg.type() === 'error') {
    console.log('Browser error:', msg.text());
  }
});

// Bắt failed network requests
page.on('requestfailed', request => {
  console.log('Failed request:', request.url());
});

Bước 5: Sử dụng expect với timeout tùy chỉnh

// Tăng timeout cho các thao tác phức tạp
await expect(page.getByText('Kết quả')).toBeVisible({ timeout: 10000 });

// Hoặc cấu hình global trong playwright.config.ts
export default defineConfig({
  expect: {
    timeout: 10000,
  },
});

Cấu hình Playwright tối ưu để giảm Flaky Test

 
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  
  // Retry test thất bại (quan trọng cho CI/CD)
  retries: process.env.CI ? 2 : 0,
  
  // Giới hạn worker để tránh race condition trên CI
  workers: process.env.CI ? 1 : undefined,
  
  // Timeout hợp lý
  timeout: 30000,
  expect: { timeout: 5000 },
  
  use: {
    // Bật trace khi retry
    trace: 'on-first-retry',
    
    // Screenshot khi test fail
    screenshot: 'only-on-failure',
    
    // Video khi retry
    video: 'on-first-retry',
    
    // Viewport nhất quán
    viewport: { width: 1280, height: 720 },
    
    // Base URL
    baseURL: 'http://localhost:3000',
  },
});

Checklist Debug Playwright Flaky Test

Trước khi kết luận test đã ổn định, hãy kiểm tra:

  • Không sử dụng waitForTimeout() thay bằng auto-wait actions
  • Locator dùng getByRole, getByLabel, hoặc getByTestId
  • Mỗi test tự thiết lập và dọn dẹp state của mình
  • Assertion dùng expect() với retry, không assert trực tiếp
  • Test chạy pass khi --repeat-each=10
  • Trace Viewer không có lỗi network hoặc console error bất ngờ
  • Không có dependency giữa các test case
  • Timeout được cấu hình phù hợp với môi trường CI

Kết luận

Debug playwright flaky test là kỹ năng quan trọng để xây dựng test suite đáng tin cậy. Ba nguyên tắc cốt lõi cần nhớ:

  1. Tin tưởng vào auto-waiting: Playwright đã xử lý phần lớn timing issues nếu bạn dùng đúng API
  2. Dùng semantic locator: getByRole, getByLabel bền vững hơn CSS/XPath
  3. Cô lập test data: Mỗi test phải độc lập, không phụ thuộc vào test khác

Với những kỹ thuật trên, bạn có thể giảm thiểu flaky test đáng kể và xây dựng automation test suite chuyên nghiệp.

🚀 Trở thành Automation Tester chuyên nghiệp với Playwright

Bạn muốn nắm vững Playwright từ cơ bản đến nâng cao — bao gồm debug flaky test, CI/CD integration, và API testing?

Trở thành Automation Test with Playwright tại CO-WELL Tech Academy với khóa học được thiết kế bởi các chuyên gia có kinh nghiệm thực chiến trong ngành.

Khóa học bao gồm:

  • Playwright fundamentals & advanced patterns
  • Debug kỹ thuật chuyên sâu với Trace Viewer & Inspector
  • Page Object Model (POM) và Test Architecture
  • CI/CD integration với GitHub Actions
  • API Testing & Mock với Playwright
  • Dự án thực tế với code review từ mentor

👉 Đăng ký ngay tại CO-WELL Tech Academy để bắt đầu hành trình trở thành Automation Test Engineer chuyên nghiệp!

Bạn đang có băn khoăn nào cần được giải đáp?

Flaky test là test không cho kết quả nhất quán — đôi khi pass, đôi khi fail mà không có thay đổi code. Bạn có thể nhận biết qua dấu hiệu: test fail trên CI nhưng chạy tay lại thì pass, hoặc fail chỉ ở một số worker khi chạy parallel. Playwright cung cấp option --repeat-each để tái hiện flaky test

waitForTimeout() là hard-coded delay — test luôn chờ đúng khoảng thời gian đó dù UI đã sẵn sàng từ trước. Điều này vừa làm test chậm không cần thiết, vừa không đáng tin cậy (nếu UI chậm hơn dự kiến vẫn fail). Thay vào đó hãy dùng waitForSelector, waitForResponse, hoặc các expect assertion của Playwright — chúng tự động chờ đến khi điều kiện thỏa mãn.

Playwright khuyến nghị dùng role-based locators như getByRole(), getByLabel(), getByText() vì chúng phản ánh cách người dùng thực sự tương tác với UI và ổn định hơn với thay đổi DOM. Nếu cần locator bền vững hơn, hãy phối hợp với team dev thêm data-testid vào element và dùng getByTestId().

Quy trình debug gợi ý: 1. Chạy test với --debug flag để mở Playwright Inspector 2. Bật trace: 'on' trong config, sau đó xem Trace Viewer 3. Thêm screenshot: 'on' để chụp ảnh từng bước 4. Kiểm tra console logs và network requests trong Trace Viewer 5. Dùng --repeat-each=10 để tái hiện flakiness

POM không bắt buộc, nhưng rất được khuyến nghị cho dự án có quy mô từ trung bình trở lên. POM giúp: tránh duplicate code, dễ maintain khi UI thay đổi, test code dễ đọc hơn. Với dự án nhỏ hoặc test đơn giản, bạn có thể bắt đầu không dùng POM và refactor dần khi cần.

Có. Cấu hình trong playwright.config.ts: typescriptretries: 2,Thử lại tối đa 2 lần khi fail Nên dùng retries: 2 trên CI và retries: 0 khi phát triển local để không che giấu flaky test trong quá trình viết code.

page.click(selector) là API cũ, tìm element theo selector và click ngay. locator.click() dùng Locator API mới hơn — lazy evaluation, không tìm element ngay mà chỉ định nghĩa "element nào sẽ tương tác". Locator API ổn định hơn, hỗ trợ auto-waiting tốt hơn và được Playwright khuyến nghị dùng.

Một số chiến lược: Mỗi test tạo dữ liệu riêng (user, order...) qua API và xóa sau khi test xong Dùng unique identifier cho dữ liệu test: user_${Date.now()}@test.com Cấu hình test database riêng cho môi trường CI Dùng database transaction và rollback sau mỗi test (với backend hỗ trợ)

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Các tin tức khác