iT邦幫忙

2023 iThome 鐵人賽

DAY 17
0

學習原因:

如果在上篇學習以後,有嘗試找 Element 來進行操作,你可能會發現有時候 Element 是存在的卻說找不到,或是找到 Element 卻不能操作等的情況。這些都是系統執行太快,而這些 Element 還沒準備好被運用,要解決這些問題,我們需要了解  Selenium 的等待機制。

學習目標:

  • 認識 Implicit Wait (隱含等待) 與 Explicit Wait (顯性等待)
  • 正確運用等待機制

Implicit Wait (隱含等待) 與 Explicit Wait (顯性等待)

在使用 Locator 找尋 Element 的時候,有 2 種的等待機制。

在上一篇文章應用的範例都是隱含等待,在 implicit wait 中,等待的時間是全局性的,它會影響整個 WebDriver 的生命週期,直到被重新設置為止。

預設等待時間為 0,代表不會等待,會立即嘗試找元素。如果找不到元素,它將立即返回一個錯誤。
隱含等待的時間可以修改,但會是全域的改變。

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.implicitly_wait(5)  # 將 implicit wait 設置為 5 秒

driver.get('https://example.com')

# driver 的 find_element() 會根據 implicit wait 設定的時間作等待。
element = driver.find_element(By.ID, 'non_existent_element')

driver.quit()

explicit wait 則可以獨立設定 等待條件時間,找到 Element 就會停止等待並回傳。

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')

# 定義等待條件,等待元素的可見性
wait = WebDriverWait(driver, 10)  # 最多等待 10 秒
element = wait.until(EC.visibility_of_element_located((By.ID, 'myButton')))

# 元素出現後進行相應操作
element.click()
driver.quit()

支援的等待條件有很多,可以參看官方的文件,這裡會特別介紹 2 個常用的:

  • EC.visibility_of_element_located: 這個等待條件檢查指定的元素是否可見,即該元素在頁面中可見,且不被隱藏。如果元素存在但被隱藏,這個等待條件將等待元素可見為止。
  • EC.element_to_be_clickable: 用於等待一個元素在頁面上變得可點擊。這個條件確保元素在可見狀態下且不被禁用,以及可以接受點擊操作。除了 Button,其他互動元件都可以應用這條件作等待,以確保該元件已經可以互動操作。

所以如果只要確認元件是否出現了,會用 EC.visibility_of_element_located 作條件。
而想等待一個元件進行操作互動,會使用 EC.element_to_be_clickable 作條件

Implicit Wait 與 Explicit Wait 的比較:

  • Implicit Wait 是全域設定等待時間,而 Explicit Wait 可根據需求個別設定不同的條件和時間。
  • Implicit Wait 僅能確認 Element 是否存在,但 Explicit Wait 可進一步確認 Element 是否可見或操作。
  • Implicit Wait 的編寫相對簡單,因為 timeout 時間只需宣告一次,找 Element 也不需逐一設定條件。而 Explicit Wait 在這方面比較麻煩,但這個麻煩其實可以透過程式設計,封裝成易用的寫法。

https://ithelp.ithome.com.tw/upload/images/20230919/20162038RGcAPJ6V33.png

常見問題 1: Implicit Wait 和 Explicit Wait 可以混用嗎?
在 Selenium 的官方文件上有提到混用 Explicit WaitImplicit Wait 可能會導致非預期的等待時間。

"WARNING: Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times. For example setting an implicit wait of 10s and an explicit wait of 15 seconds, could cause a timeout to occur after 20 seconds."

是說當 Implicit Wait 設定為 10s 時, explicit Wait 等待 15s
等待 10s 之後,element 還沒出現,在繼續等待的情況下,Implicit wait 會重新計算 10s,所以在 element 最後都沒有被找到的情況下,會等到 20s

常見問題 2: 可以直接用 time.sleep() 等待元件嗎?
盡可能不要!這種是硬性等待,不管 Element 出現了沒,都要繼續等。一來是你無法預知到底要等多久才會出現,二來是大部分的 Element 其實都不需要找很久,應用 time.sleep() 會拖慢整個測試,非常沒有效率,所以非必要都不要使用。

因此,在 Implicit WaitExplicit Wait 的比較之下,還是會建議用 Explicit Wait 等待機制,可根據需求設定各 Element 的等待方式,可以得到更精準的時間點去作驗證或是操作元件。至於寫法麻煩,以下提供一個例子作參考,去簡化需要編寫的流程。

這例子以 https://demo.applitools.com/ 作示範

import time

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

driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://demo.applitools.com/")

# 應用 EC.visibility_of_element_located 確認元件已顯示
header = WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.CLASS_NAME, "auth-header"))
)
print(header.text)

# 需要互動的元件,應用 EC.element_to_be_clickable
username_elem = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "username"))
)
username_elem.send_keys("username")

pw_elem = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "password"))
)
pw_elem.send_keys("password")

checkbox_elem = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.CLASS_NAME, "form-check-input"))
)
checkbox_elem.click()

login_btn_elem = WebDriverWait(driver, 10).until(
    EC.element_to_be_clickable((By.ID, "log-in"))
)
login_btn_elem.click()

# 設定 10s 停頓,讓你可以觀察頁面的操作,正常測試時請拿掉,以免浪費測試時間
time.sleep(10)
driver.quit()

以上例子應用了 Explicit Wait,只是執行 5 個動作就要花這麼大的篇幅去寫。

現在會把重覆性高的程式碼封裝成 Function,以提高程式碼的重用性,以簡化主程式。

import time

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

# 主要會用 2 種的等待條件,寫成 2 個 Function
# locator 和 timeout 作為參數,可以分別指定尋找不同的 Element 以及相應的等待時間
# 等待時間可以不設定,預設為 10s
def find_visible_element(locator, timeout=10):
    return WebDriverWait(driver, timeout).until(
        EC.visibility_of_element_located(locator)
    )

def find_clickable_element(locator, timeout=10):
    return WebDriverWait(driver, timeout).until(
        EC.element_to_be_clickable(locator)
    )

driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://demo.applitools.com/")

header = find_visible_element((By.CLASS_NAME, "auth-header"))
print(header.text)

username_elem = find_clickable_element((By.ID, "username"))
username_elem.send_keys("username")

pw_elem = find_clickable_element((By.ID, "password"))
pw_elem.send_keys("password")

checkbox_elem = find_clickable_element((By.CLASS_NAME, "form-check-input"))
checkbox_elem.click()

login_btn_elem = find_clickable_element((By.ID, "log-in"))
login_btn_elem.click()

time.sleep(10)
driver.quit()

現在還會覺得應用 Explicit Wait 很麻煩嗎?

透過程式設計,我們可以提高程式碼的重用性和靈活性,如同之前介紹 OOP 的目的,下一篇文章,我們會再來了解 POM (Page Object Model) 怎樣被套用在自動化測試的程式上。


上一篇
Day 16: Selenium - Locator Strategy
下一篇
Day 18: Page Object Model
系列文
從 0 開始培育成為自動化測試工程師的學習指南30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言