Waits

  1. Selenium Implicit Waits
  2. Selenium Explicit Waits
  3. Selenium Fluent Waits
  4. Selenium Implicit Waits and Explicit Waits together
  5. Playwright Waits

Waits bản chất là một tính năng hỗ trợ timing, hiểu nôm na là “đợi”. Trong Browser Automation, chúng ta sẽ sử dụng Waits để đợi cho đến khi một điều kiện nào đó được thoả mãn. Lý do tại sao lại cần waits đó là để tính đến khả năng của đường truyền mạng hoặc tốc độ xử lý của trình duyệt, một trường hợp hay gặp chính là:

Cần xác định xem một element đã hiển thị hay chưa, nhưng do đường truyền mà element đó không hiển thị ngay lập tức, nếu chúng ta không sử dụng waits mà thực hiện kiểm tra ngay lập tức thì sẽ luôn báo lỗi

Có hai hướng Waits hiện nay tuỳ theo kiểu công nghệ mà các framework sử dụng:

  • Framework như Selenium sử dụng Web Driver để tương tác với trình duyệt, nó sẽ có một số loại waits như:
    • Implicit Waits
    • Explicit Waits
    • Fluent Waits
  • Framework như Puppeteer sử dụng API của trình duyệt (Chrome Dev Tool đối với trình duyệt sử dụng nhân Chromium), nó sẽ cung cấp một số loại waits như:
    • waitForSelector
    • waitForXPath
    • waitForFunction
  • Framework như Playwright thì hoàn toàn tự động hóa việc waits, không cần phải quan tâm đến việc waits - sử dụng autowaits

Bài viết này thì mình sẽ nói về waits của Selenium và Playwright

Selenium Implicit Waits

Implicit Waits ở trong Selenium là một loại waits thông qua tương tác với WebDriver, nó sẽ đặt cho session của WebDriver một thời gian chờ trước khi ném ra lỗi NoSuchElementException. Bình thường nếu không đặt implicit waits, khi chúng ta tương tác với một element bằng selector, nếu selector này không tìm thấy element nào thì lỗi trên sẽ được trả ra ngay lập tức.

Implicit Waits cho phép một khoảng thời gian để trang web load hoàn toàn, hoặc cho các code javascript chạy xong, trước khi chúng ta thực hiện tương tác với element.

Trong Selenium, để sử dụng Implicit Waits, chúng ta sẽ sử dụng driver.implicitly_wait(time), với time là thời gian chờ, đơn vị là giây.

from selenium import webdriver

driver = webdriver.Chrome()
driver.implicitly_wait(10) # 10 seconds

Trong Robot Framework, library SeleniumLibrary cung cấp keyword Set Browser Implicit Wait để sử dụng Implicit Waits

*** Settings ***
Library    SeleniumLibrary

*** Test Cases ***
Example
    Set Browser Implicit Wait    10
    Open Browser    https://google.com    chrome

Ngoài ra Robot Framework cũng cung cấp từ khoá mở rộng Set Selenium Implicit Wait để sử dụng Implicit Waits, về cơ bản là có bao nhiêu trình duyệt đang mở thì sẽ được áp dụng Implicit Waits cho tất cả, còn Set Browser Implicit Wait thì chỉ áp dụng cho trình duyệt đang được tập trung

Ngoài ra, nếu chỉ cần quan tâm đến chuyện page load xong thì có thể sử dụng driver.set_page_load_timeout(time), nó sẽ đặt thời gian chờ cho page load xong, nếu quá thời gian chờ mà page chưa load xong thì sẽ ném ra lỗi TimeoutException

Selenium Explicit Waits

Explicit Waits thực chất là một loại waits mà chúng ta sẽ đặt cho một element cụ thể, nó sẽ chờ cho đến khi element đó thoả mãn một điều kiện nhất định. Explicit Waits ám chỉ đến cách chúng ta viết code và handle các hàm assert chứ nó không phải là một function trọn vẹn như Implicit Waits.

Phương pháp sử dụng Explicit Waits trong Selenium là dựa vào hàm wait_until của WebDriverWait và expected_conditions của selenium.webdriver.support.ui để xác định điều kiện cụ thể.

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://example.com")

# Create an instance of WebDriverWait with a timeout of 10 seconds
wait = WebDriverWait(driver, 10)

# Wait until the element is visible
element = wait.until(EC.visibility_of_element_located((By.ID, "elementId")))

# Perform actions on the element
element.click()

# Close the browser
driver.quit()

Trong Robot Framework, library SeleniumLibrary cung cấp các keyword Wait Until, về cơ bản cũng chỉ gói lại function wait_until của WebDriverWait và expected_conditions của selenium.webdriver.support.ui

*** Settings ***
Library    SeleniumLibrary

*** Test Cases ***
Example Test Case
    Open Browser    https://example.com    chrome
    Wait Until Element Is Visible    id=elementId    10 seconds
    Click Element    id=elementId
    Close Browser

Nôm na tóm lại, Explicit Waits là việc chúng ta đặt một điều kiện cụ thể cho một element, và chờ cho đến khi điều kiện đó thoả mãn, nếu trong thời gian chờ mà điều kiện không thoả mãn, chúng ta đánh fail. Và Explicit Wait có thể hiểu là nằm ở phía IDE, chứ không phải là một tính năng cụ thể của Web Driver.

Selenium Fluent Waits

Fluent Waits là một loại wait đi ra từ Explicit Waits nhưng với nhiều tuỳ chỉnh thông số: polling interval, timeout , ignore exceptions. Fluent Waits cho phép chúng ta tùy chỉnh thời gian chờ, thời gian polling, loại exception bỏ qua.

Trong Selenium, để sử dụng Fluent Waits, chúng ta sẽ sử dụng WebDriverWait và expected_conditions như trong Explicit Waits, nhưng thêm một số tuỳ chỉnh như poll_frequency, ignored_exceptions

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
import time

driver = webdriver.Chrome()
driver.get("https://example.com")

# Define a custom wait function
def wait_for_element(driver):
    element = driver.find_element(By.ID, "elementId")
    if element.is_displayed():
        return element
    else:
        return False

# Create an instance of WebDriverWait with a timeout of 10 seconds and polling every 2 seconds
wait = WebDriverWait(driver, 10, poll_frequency=2, ignored_exceptions=[NoSuchElementException])

# Wait until the element is visible
element = wait.until(wait_for_element)

# Perform actions on the element
element.click()

# Close the browser
driver.quit()

Thực tế mà nói thì cách ứng dụng Fluent Waits trong Selenium Python là kiểu mì ăn liền, còn gốc gác bên java thì phức tạp hơn

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import java.time.Duration;
import java.util.NoSuchElementException;

public class FluentWaitExample {
    public static void main(String[] args) {
        WebDriver driver = new ChromeDriver();
        driver.get("https://example.com");
        
        // Create an instance of FluentWait with a timeout of 10 seconds and polling every 2 seconds
        FluentWait<WebDriver> wait = new FluentWait<>(driver)
            .withTimeout(Duration.ofSeconds(10))
            .pollingEvery(Duration.ofSeconds(2))
            .ignoring(NoSuchElementException.class);
        
        // Wait until the element is visible
        WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("elementId")));
        
        // Perform actions on the element
        element.click();
        
        // Close the browser
        driver.quit();
    }
}

Selenium Implicit Waits and Explicit Waits together

Bản chất Implicit Waits là tính năng thuộc Web Driver, còn Explicit Waits là thuộc về cách code - tức là IDE, vốn không liên quan gì đến nhau. Vì thế, hành vi của bài test khi thực hiện cùng lúc cả hai loại waits này sẽ rơi vào vùng không xác định - tức là nhà phát triển Selenium không hề đặt ra một quy luật cụ thể mà thời gian chờ kết hợp diễn ra đơn thuần do logic của code, tuỳ thuộc vào cách các function liên quan được viết mà nó có thể thay đổi tuỳ theo phiên bản của Selenium.

Hiện tại, với Robot Framework, việc sử dụng cả hai waits sẽ diễn ra như sau:

  • Explicit Waits sẽ bọc lấy Implicit Waits
  • Trong khoảng thời gian khả dụng của explicit waits, implicit waits sẽ được reset mỗi khi hết hạn

Ví dụ, với một test case trong Robot Framework:

*** Settings ***
Library    SeleniumLibrary

*** Test Cases ***
Example Test Case
    Set Browser Implicit Wait    8
    Open Browser    https://example.com    chrome
    Wait Until Element Is Visible    id=elementId    10 seconds
    Click Element    id=elementId
    Close Browser

Điều xảy ra sẽ là:

  • Ở 0 giây, implicit waits được set là 8 giây, explicit waits được set là 10 giây
  • Ở 8 giây, implicit waits hết hạn và đúng ra sẽ throw NoSuchElementException nhưng vì explicit waits còn 2 giây nên keyword Wait Until Element Is Visible vẫn chưa fail, implicit waits reset về 0 giây
  • Ở giây thứ 10, explicit waits hết hạn, nhưng chưa có fail do bước fail do element không tồn tại vẫn đang được chờ bởi implicit waits
  • Ở giây thứ 16 (8 + 8), implicit waits hết hạn và throw NoSuchElementException, bây giờ Explicit waits đã có đủ điều kiện để fail nên cũng fail luôn

Tổng thời gian là 16 giây + thêm vài giây delay và nhảy polling ở giữa.

Tóm lại, không nên sử dụng cả hai loại waits cùng lúc, lý do là vì trong tình huống ở trên, đúng ra test case nên fail trong vòng 10 theo ý tưởng của người code, nhưng do hành vi bất thường của Selenium nên test case fail sau 16 giây, nó sẽ gây ảnh hưởng đến cái nhìn tổng thể về sản phẩm và gây ra kết quả dường như không đáng tin.

Playwright Waits

Playwright là một framework mới ra đời, nó tự động hóa việc waits, không cần phải quan tâm đến việc waits, nó sẽ tự động chờ cho đến khi element được tìm thấy, hoặc một điều kiện nào đó được thoả mãn, ví dụ:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  
  // Wait for the element to be visible
  await page.waitForSelector('#elementId');
  
  // Perform actions on the element
  await page.click('#elementId');
  
  await browser.close();
})();

Nó được thể hiện ở các function như waitForSelector, waitForXPath, waitForFunction, waitForLoadState, waitForNavigation, waitForRequest, waitForResponse, waitForEvent, waitForTimeout, waitForURL, v.v.. mây mây. Đánh giá sơ bộ của mình là dùng playwright rất sướng, nó tương tác với trình duyệt thông qua CDP nên tốc độ và thời gian waits cũng rất chính xác, đồng thời tránh được những lỗi không mong muốn khi sử dụng Selenium.