⚙️ Khi thực hiện kiểm thử tự động Web, một trong những vấn đề thường gặp là timing.
Một phần tử có thể chưa xuất hiện, chưa hiển thị, đang được tải lên, chưa thể click, hoặc dữ liệu trên trang chưa được cập nhật tại thời điểm test case thực hiện thao tác. Nếu không xử lý tốt những tình huống này, test case tự động của bạn có thể thất bại dù chức năng của ứng dụng vẫn hoạt động đúng.
Playwright cung cấp nhiều cơ chế giúp việc kiểm thử tự động Web trở nên ổn định hơn. Trong đó, Auto-waiting là một trong những cơ chế quan trọng. Nếu bạn mới tìm hiểu Playwright, có thể xem trước bài Playwright là gì?.
Auto-waiting là gì?
Trong Playwright, auto-waiting là một cơ chế hỗ trợ tự động chờ cho đến khi một phần tử đáp ứng các điều kiện cần thiết trước khi thực hiện một action.
Ví dụ, với:
await page.getByRole('button', { name: 'Login' }).click();
Playwright không đơn giản tìm button có tên Login, rồi click ngay lập tức.
Trước khi click, Playwright sẽ thực hiện các kiểm tra cần thiết, chẳng hạn:
- Locator phải xác định được đúng một phần tử.
- Phần tử phải hiển thị.
- Phần tử phải ổn định, không đang chuyển động hoặc animation.
- Phần tử phải nhận được sự kiện tương tác.
- Phần tử phải được enable.
Nếu các điều kiện chưa đáp ứng, Playwright sẽ tiếp tục chờ trong khoảng timeout cho phép. Nếu vẫn không đáp ứng, action sẽ thất bại với TimeoutError.
Có thể hình dung đơn giản:
Test case yêu cầu click → Tìm phần tử → Kiểm tra trạng thái phần tử →
- Đã sẵn sàng → Thực hiện click
- Chưa sẵn sàng → Tiếp tục chờ → Hết thời gian chờ (timeout) → báo lỗi TimeoutError
Vì sao Auto-waiting quan trọng trong kiểm thử tự động?
Trong ứng dụng Web hiện đại, giao diện thường được cập nhật bất đồng bộ.
Ví dụ:
- Tester mở trang Login.
- Trang bắt đầu tải.
- Button Login được render.
- Một đoạn JavaScript tiếp tục xử lý.
- Button mới được enable.
- Người dùng có thể click.
Nếu test case cố gắng click button ở bước 3 trong khi button chưa sẵn sàng ở bước 5, test case có thể thất bại.
Một cách xử lý quen thuộc trong một số công cụ automation là thêm thời gian chờ cố định:
await page.waitForTimeout(3000);
await page.getByRole('button', { name: 'Login' }).click();
Nhưng cách này có một vấn đề: 3 giây không nhất thiết là thời gian ứng dụng cần.
Nếu ứng dụng sẵn sàng sau 500 ms, test case đã chờ lâu hơn cần thiết. Nếu ứng dụng mất 5 giây, 3 giây vẫn chưa đủ.
Auto-waiting giúp Playwright chờ theo trạng thái cần thiết của phần tử, thay vì chỉ chờ một khoảng thời gian cố định.
Auto-waiting khác gì waitForTimeout()?
Đây là điểm quan trọng đối với người mới học Playwright.
waitForTimeout()
await page.waitForTimeout(3000);
await page.getByRole('button', { name: 'Login' }).click();
Ý nghĩa: Chờ đúng 3 giây rồi click. Test case không thực sự biết button đã sẵn sàng hay chưa.
Auto-waiting
await page.getByRole('button', { name: 'Login' }).click();
Ý nghĩa: Thực hiện action khi phần tử đáp ứng các điều kiện cần thiết. Playwright sẽ tự động thực hiện kiểm tra trạng thái có thể hành động trước khi click.
Vì vậy, trong nhiều trường hợp, không cần thêm waitForTimeout() chỉ để chờ phần tử sẵn sàng.
Tài liệu Playwright cũng khuyến nghị sử dụng Locator và web-first assertions thay vì dựa vào các cách chờ thủ công không cần thiết.
Playwright tự động kiểm tra những gì?
Các action khác nhau có thể có những điều kiện kiểm tra khác nhau.
Ví dụ, với locator.click(), Playwright kiểm tra:
1. Locator xác định đúng phần tử
Playwright cần xác định được phần tử phù hợp để thực hiện action.
Ví dụ:
const loginButton = page.getByRole('button', { name: 'Login' });
await loginButton.click();
Nếu locator không xác định được phần tử phù hợp trong thời gian cho phép, action sẽ thất bại.
2. Phần tử phải Visible
Phần tử cần ở trạng thái có thể nhìn thấy theo các tiêu chí của Playwright.
Ví dụ, một element có: display: none; không được xem là visible.
3. Phần tử phải Stable
Phần tử không được đang thay đổi vị trí hoặc kích thước do animation.
Playwright xác định một element là stable khi bounding box của nó giữ nguyên trong ít nhất hai animation frames liên tiếp.
4. Phần tử phải nhận được sự kiện
Nếu có một element khác, chẳng hạn overlay hoặc popup, che lên vị trí cần click thì phần tử mục tiêu có thể không nhận được click.
Playwright sẽ kiểm tra điều kiện này trước khi thực hiện action.
5. Phần tử phải Enabled
Ví dụ:
<button disabled>Login</button>
Button đang disabled thì chưa thể thực hiện action click thông thường.
Playwright sẽ chờ cho đến khi điều kiện cần thiết được đáp ứng hoặc timeout xảy ra.
Auto-waiting không chỉ dành cho click()
Auto-waiting được áp dụng cho nhiều action của Locator.
Ví dụ:
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByLabel('Email').fill('tester@example.com');
await page.getByLabel('Country').selectOption('Vietnam');
Tùy từng action, Playwright thực hiện những cách kiểm tra trạng thái có thể hành động khác nhau.
Ví dụ,
- click() kiểm tra nhiều điều kiện như Visible, Stable, Receives Events, và Enabled;
- trong khi đó, fill() tập trung vào những điều kiện phù hợp với việc nhập dữ liệu, chẳng hạn phần tử phải visible, enabled, và editable.
Do đó, tester không cần xây dựng một chuỗi wait thủ công trước mỗi hành động.
Auto-waiting và Assertion
Auto-waiting không chỉ xuất hiện trước các action.
Playwright còn cung cấp các web-first assertions có khả năng tự động retry cho đến khi điều kiện đạt được hoặc timeout xảy ra.
Ví dụ:
await expect(page.getByText('Login successful')).toBeVisible();
Giả sử thông báo Login successful chỉ xuất hiện sau khi hệ thống xử lý xong request.
Playwright sẽ không chỉ kiểm tra một lần rồi kết luận thất bại. Assertion sẽ tiếp tục kiểm tra cho đến khi điều kiện đúng hoặc timeout.
Một ví dụ khác:
await expect(page.getByRole('heading')).toHaveText('Dashboard');
Playwright sẽ tiếp tục kiểm tra nội dung của element cho đến khi nội dung phù hợp với kết quả mong đợi hoặc timeout xảy ra.
Đây là một điểm quan trọng: Auto-waiting giúp Playwright chờ phần tử sẵn sàng để thực hiện action; web-first assertions giúp Playwright chờ trạng thái mong đợi của ứng dụng.
Không nên lạm dụng waitForTimeout()
Một test case như sau thường không phải lựa chọn tốt:
await page.getByRole('button', { name: 'Submit' }).click();
await page.waitForTimeout(5000);
await expect(page.getByText('Success')).toBeVisible();
Ở đây, test case đang giả định rằng hệ thống sẽ hoàn thành trong 5 giây. Nhưng nếu hệ thống hoàn thành sau 1 giây, test case vẫn chờ thêm. Nếu hệ thống mất 6 giây, test case có thể thất bại.
Trong nhiều trường hợp, nên mô tả điều tester thực sự muốn kiểm tra:
await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByText('Success')).toBeVisible();
Cách này giúp test case thể hiện rõ kết quả mong đợi, ví dụ: Sau khi bấm nút Submit, thông báo Success phải xuất hiện. Playwright sẽ tự động đánh giá lại cho đến khi điều kiện đạt được hoặc hết thời gian chờ (timeout).
Khi nào cần chờ thủ công?
Auto-waiting không có nghĩa là không bao giờ cần sử dụng các API wait.
Có những tình huống tester thực sự cần chờ một điều kiện cụ thể.
Ví dụ, có thể cần chờ URL:
await page.waitForURL('**/dashboard');
Hoặc chờ một response cụ thể:
await page.waitForResponse('**/api/login');
Điểm quan trọng là nên chờ điều kiện có ý nghĩa đối với test case, thay vì chờ một khoảng thời gian cố định.
Ngoài ra, tài liệu Playwright lưu ý rằng waitForLoadState() thường không cần thiết trong nhiều trường hợp vì Playwright đã tự động chờ trước các action.
Auto-waiting có giải quyết mọi vấn đề timing không?
Không.
Auto-waiting giúp Playwright xử lý nhiều vấn đề liên quan đến timing, nhưng không có nghĩa mọi test case đều tự động ổn định.
Test case vẫn có thể thất bại do:
- Locator không chính xác hoặc không ổn định.
- Ứng dụng thực sự có lỗi.
- Mong đợi không đúng.
- Dữ liệu test không phù hợp.
- Chờ sai điều kiện.
- Logic của test case có vấn đề.
Playwright cũng khuyến nghị sử dụng Locator phù hợp. Locator là một thành phần quan trọng của cơ chế auto-waiting và retry-ability trong Playwright.
Ví dụ:
await page.getByRole('button', { name: 'Submit' }).click();
thường có ý nghĩa rõ ràng hơn việc phụ thuộc vào một selector CSS dễ thay đổi:
await page.locator('.btn-primary-123').click();
Auto-waiting và việc viết test case ổn định
Một test case tự động ổn định không chỉ phụ thuộc vào Auto-waiting.
Tester cần kết hợp nhiều yếu tố: Locator phù hợp + Auto-waiting + Web-first assertions + Test data phù hợp + Mong đợi rõ ràng → Test case ổn định hơn
Auto-waiting là một cơ chế hỗ trợ quan trọng, nhưng không thay thế kỹ năng thiết kế test case và phân tích ứng dụng của tester.
Kết luận
Playwright Auto-waiting là cơ chế giúp Playwright tự động chờ phần tử đáp ứng các điều kiện cần thiết trước khi thực hiện action. Với các action như click(), Playwright có thể kiểm tra trạng thái Visible, Stable, Receives Events và Enabled trước khi thực hiện thao tác.
Playwright cũng cung cấp các web-first assertions có khả năng tự động retry, giúp test case chờ đến khi trạng thái mong đợi của ứng dụng xuất hiện.
Vì vậy, thay vì viết nhiều đoạn:
await page.waitForTimeout(...);
Tester nên ưu tiên sử dụng Locator, Auto-waiting và web-first assertions để mô tả trực tiếp hành động và kết quả mong đợi của test case.
Nếu bạn mới bắt đầu với Playwright, có thể xem thêm bài Cài đặt Playwright và viết test đầu tiên với TypeScript và tìm hiểu các khái niệm tiếp theo như Locator và Assertion.
Nếu muốn học Playwright một cách có hệ thống với TypeScript, bạn có thể tham khảo Khóa học Playwright – Kiểm thử tự động Web với TypeScript tại Testing VN.

