Page Object Model(POM)是一種在自動化測試中使用的軟體測試設計模式,主要用於將 Test Case 的邏輯和網頁介面的元素分離,提高測試程式的可讀性
和維護性
。
POM 的核心概念是將每個網頁視為一個 頁面 (Page),並為每個頁面創建一個對應的 Page Object。每個 Page Object 類封裝了該頁面的 元素定位 和 操作方法,這樣測試代碼只需調用 Page Object 的方法,而不需要直接處理元素定位等細節。
以上一篇文章已簡單優化過的程式,作為例子:
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
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()
改寫成 Page Object Model,把 Login 頁面寫成 Page Object。
from selenium.webdriver.common.by import By
from action_utils import ActionUtils
# 這裡會繼承 ActionUtils class,裡面會有針對 WebDriver 的操作方法,如找 Element 的方法等
# 下面會再詳述
class LoginPage(ActionUtils):
# 把 Locator 以 Tuple 型態存儲成 Login Page 的 attribute
# 可被 Page Object 內的 method 運用
header = (By.CLASS_NAME, "auth-header")
username_text_field = (By.ID, "username")
pw_text_field = (By.ID, "password")
remember_me_checkbox = (By.CLASS_NAME, "form-check-input")
login_btn = (By.ID, "log-in")
# 頁面操作相關的 methods
def print_header_text(self):
print("印出 Header 的文字")
# 應用了父層 ActionUtils 的 Method
print(self.find_visible_elem(self.header).text)
def input_username(self, username):
print(f"輸入 Username: {username}")
username_elem = self.find_clickable_elem(self.username_text_field)
username_elem.send_keys(username)
def input_password(self, password):
print(f"輸入 Password: {password}")
pw_elem = self.find_clickable_elem(self.pw_text_field)
pw_elem.send_keys(password)
def check_remember_me(self):
print("勾選 Remember me")
self.find_clickable_elem(self.remember_me_checkbox).click()
def click_login_btn(self):
print("點擊登入按鈕")
self.find_clickable_elem(self.login_btn).click()
而之前抽出的 find_visible_elem()
和 find_clickable_elem()
,因為可以被多個不同的 Page Object 應用,所以會建一個 Page Object 的 Parent,我這裡命名 ActionUtils
,也有蠻多人會命名為 PageBase
,都只供參考。
每個 Page Object 都會繼承 ActionUtils
以運用共用的 method。
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class ActionUtils():
# 頁面的操作需要 Webdriver,所以需要主程式從參數傳入
def __init__(self, driver):
self.driver = driver
def find_visible_elem(self, locator, timeout=10):
return WebDriverWait(self.driver, timeout).until(
EC.visibility_of_element_located(locator)
)
def find_clickable_elem(self, locator, timeout=10):
return WebDriverWait(self.driver, timeout).until(
EC.element_to_be_clickable(locator)
)
# 後續可擴充更多 Page Object 共用的 Method,都是應用 WebDriver 的相關操作。
Page Object 都寫好了,再來就是修改主程式。
import time
from selenium import webdriver
from login_page import LoginPage
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("https://demo.applitools.com/")
# init login page object
login_page = LoginPage(driver)
# 運用 page object 的 method 完成了整個操作流程
login_page.print_header_text()
login_page.input_username("username")
login_page.input_password("password")
login_page.check_remember_me()
login_page.click_login_btn()
time.sleep(10)
driver.quit()
可見主程式少了很多雜訊,從這段程式更簡單的看出操作的步驟。
後續可能會覺得登入流程幾乎是每個 Test Case 都必須的組合流程,甚至會可以在 Page Object 裡面再多包一個 Method,然後就可被主程式直接運用。
class LoginPage(ActionUtils):
...
def login(self, username, password):
self.input_username(username)
self.input_password(password)
self.check_remember_me()
self.click_login_btn()
簡單用一張圖來表達整體的架構
使用 Page Object Model 優點:
提高可讀性和維護性:
測試程式更清晰、易讀,且易於維護,因為元素定位和操作都在 Page Object 中集中處理。
重用性:
如果頁面元素或功能發生變化,只需更新相應的 Page Object,不需修改多個測試案例。
降低耦合度:
可以更專注於測試邏輯,不需了解頁面結構和元素定位的細節,只需調用 Page Object 方法。
學會 Page Object Model 的寫法,就繼續用昨天建議的網站,寫成 Page Object Model 練習吧。
Web 的 UI 測試先到這裡了,接下來會進入 API 測試的部分。