Khi viết automation testing với Playwright, nhiều anh em thường sử dụng networkidle
như một cách để "chờ" cho đến khi trang web sẵn sàng trước khi thực hiện các hành động tiếp theo.
Tuy nhiên, đây không phải là cách làm tối ưu! Sử dụng networkidle
không chỉ tốn tài nguyên mà còn dễ gây ra lỗi không ổn định (flaky tests).
Trong bài viết này, chúng ta sẽ tìm hiểu chi tiết về networkidle
: nó là gì, tại sao nên tránh, và những phương án thay thế hiệu quả hơn.
Networkidle là gì?
Trong Playwright, networkidle
là một trạng thái được sử dụng trong hàm page.waitForNavigation()
hoặc các lệnh tương tự để chờ cho đến khi hoạt động mạng của trang web "ổn định". Cụ thể, Playwright coi trạng thái networkidle
đạt được khi:
- Không có network requests nào diễn ra trong vòng 500ms.
- Hoặc số lượng network requests đang hoạt động giảm xuống dưới một ngưỡng nhất định (thường là 0 hoặc 2, tùy thuộc vào cấu hình).
networkidle
thường được dùng để đảm bảo rằng trang web đã tải xong toàn bộ tài nguyên (HTML, CSS, JavaScript, hình ảnh, API calls, v.v.) trước khi thực hiện các hành động như click, fill, hoặc kiểm tra nội dung.
Ví dụ:
await page.goto('https://playwrightvn.com', { waitUntil: 'networkidle' });
Trong đoạn code trên, Playwright sẽ đợi cho đến khi không còn hoạt động mạng nào trong 500ms trước khi chuyển sang bước tiếp theo.
Tại sao không nên dùng networkidle
?
Mặc dù networkidle
có vẻ là một cách tiện lợi để chờ trang tải xong, nhưng nó có nhiều hạn chế nghiêm trọng, đặc biệt trong các ứng dụng web hiện đại. Dưới đây là những lý do chính:
Không đáng tin cậy (Flaky):
Các ứng dụng web ngày nay thường tải nội dung động thông qua các API hoặc WebSocket. Những yêu cầu này có thể kéo dài hoặc chạy ngắt quãng, khiến networkidle không bao giờ đạt được.
Một số trang web có các yêu cầu mạng chạy nền liên tục (ví dụ: tracking pixels, analytics, hoặc polling API), làm cho networkidle bị treo vô thời hạn hoặc thất bại.
Tốn thời gian và tài nguyên:
Chờ networkidle
có thể mất nhiều thời gian hơn cần thiết, đặc biệt khi trang web tải các tài nguyên không liên quan đến kịch bản kiểm thử (như quảng cáo, hình ảnh lớn, hoặc video).
Điều này làm chậm quá trình kiểm thử, đặc biệt trong các dự án lớn với hàng trăm test case.
Thiếu linh hoạt:
networkidle
áp dụng một quy tắc cứng nhắc (chờ 500ms không có yêu cầu mạng), không phù hợp với các trường hợp cần kiểm soát chi tiết hơn. Ví dụ, bạn có thể chỉ cần chờ một API cụ thể trả về dữ liệu, thay vì toàn bộ hoạt động mạng.
Không thể tùy chỉnh để đáp ứng các yêu cầu kiểm thử phức tạp, như chờ một phần tử xuất hiện hoặc một điều kiện JavaScript cụ thể.
Không phù hợp với ứng dụng web hiện đại:
Với các framework như React, Angular, hoặc Vue, nội dung trang thường được render phía client sau khi tải dữ liệu từ API. networkidle
không đảm bảo rằng giao diện người dùng đã sẵn sàng, dẫn đến lỗi khi thực hiện các hành động tiếp theo.
Tóm lại, việc lạm dụng networkidle
có thể khiến kịch bản kiểm thử trở nên chậm, không ổn định, và khó bảo trì.
Dùng gì thay thế networkidle?
Thay vì dựa vào networkidle
, Playwright cung cấp nhiều phương pháp thay thế mạnh mẽ và linh hoạt hơn để chờ đúng thời điểm cần thiết. Dưới đây là các giải pháp phổ biến, kèm ví dụ cụ thể:
waitForSelector
– Chờ phần tử xuất hiện trên DOM:
Nếu bạn cần đảm bảo một phần tử cụ thể (như nút, biểu mẫu, hoặc văn bản) đã xuất hiện, hãy sử dụng page.waitForSelector. Phương pháp này nhanh, chính xác và tập trung vào giao diện người dùng.
await page.waitForSelector('#submit-button', { state: 'visible' }); await page.click('#submit-button');
Khi nào dùng? Khi bạn cần chờ một phần tử cụ thể hiển thị hoặc sẵn sàng để tương tác.
waitForTimeout
– Chờ một khoảng thời gian cố định:
Trong một số trường hợp đơn giản, bạn chỉ cần chờ một khoảng thời gian ngắn để đảm bảo trang ổn định. Dùng waitForTimeout để kiểm soát chính xác thời gian chờ.
await page.waitForTimeout(1000); // Chờ 1 giây
Khi nào dùng? Khi bạn biết chắc thời gian chờ cần thiết và không muốn phụ thuộc vào trạng thái mạng.
waitForResponse
– Chờ phản hồi từ một API cụ thể:
Nếu kịch bản kiểm thử phụ thuộc vào dữ liệu từ một API, bạn có thể chờ phản hồi từ URL cụ thể bằng waitForResponse. Điều này đảm bảo rằng dữ liệu cần thiết đã được tải trước khi tiếp tục.
await page.waitForResponse('https://api.example.com/data');
Khi nào dùng? Khi bạn cần chờ một yêu cầu mạng cụ thể hoàn tất, thay vì toàn bộ hoạt động mạng.
waitForFunction
– Chờ điều kiện JavaScript tùy chỉnh:
Phương pháp này cho phép bạn chờ một điều kiện tùy chỉnh được xác định bởi một hàm JavaScript. Nó cực kỳ linh hoạt và phù hợp với các trường hợp phức tạp.
await page.waitForFunction(() => document.querySelector('#data-loaded')?.textContent === 'Ready');
Khi nào dùng? Khi bạn cần kiểm tra một trạng thái cụ thể của trang, như nội dung, thuộc tính, hoặc biến JavaScript.
Kết hợp các phương pháp: Trong nhiều trường hợp, bạn có thể kết hợp các phương pháp trên để đạt được kết quả tốt nhất. Ví dụ:
await Promise.all([ page.waitForResponse('https://api.example.com/data'), page.waitForSelector('#result', { state: 'visible' }) ]);
Khi nào dùng? Khi bạn cần đảm bảo nhiều điều kiện cùng lúc (ví dụ: API trả về và giao diện hiển thị).
Kết luận
networkidle
có thể là một lựa chọn tiện lợi trong các kịch bản đơn giản, nhưng nó không phù hợp với các ứng dụng web hiện đại và dễ gây ra các vấn đề về hiệu suất cũng như độ tin cậy. Thay vào đó, hãy sử dụng các phương pháp thay thế như waitForSelector, waitForResponse, waitForFunction
, hoặc waitForTimeout
để kiểm soát chính xác thời điểm thực hiện hành động.
Bằng cách áp dụng các phương pháp này, bạn sẽ viết được các kịch bản kiểm thử nhanh hơn, ổn định hơn, và dễ bảo trì hơn. Nếu bạn có thắc mắc hoặc cần thêm ví dụ, hãy để lại câu hỏi cho tụi mình nhé ^^.
Bài viết được trích từ blog của Playwright Việt Nam: Link
Tìm hiểu thêm các tài nguyên khác tại: Tài nguyên học tập Playwright