前言
Page Object(PO)
模式,是Selenium
實戰中最為流行,並且被自動化測試同學所熟悉和推崇的一種設計模式之一。在設計測試時,把頁面元素定位和元素操作方法按照頁面抽象出來,分離成一定的物件,然後再進行組織。
相信每個做自動化測試的同學,一定會遇到這樣一個非常頭疼的問題,那就是頁面變化,如果沒有使用Page Object
設計模式,這就意味著以前的定位元素方法不能用了,需要重新修改元素定位方式。你需要一個一個從測試指令碼中把需要修改的元素定位方式找出來,然後再進行修改。這勢必會使指令碼維護的成本變高,顯然這樣的自動化指令碼就不會有人願意使用。
那這時我們使用Page Object
模式就可以解決這個問題了。
PageObject 的優點
- 減少程式碼冗餘
- 業務和實現分離
- 降低程式碼維護成本
Page Object模式
Page Object 見名知意,就是頁面物件,並將頁面元素定位方法和元素操作進行分離。
在實際自動化測試實戰過程中,我們一般對指令碼的實現分為三層:
- 物件層:用於存放頁面元素定位和控制元件操作。
- 邏輯層:則是一些封裝好的功能用例模組。
- 業務層:則是我們真正的測試用例的操作部分。
使用 Page Object 類來分離頁面元素
物件層
首先我們新建一個類login_page
,登入頁面內編寫需要操作的元素定位方式和控制元件操作,具體程式碼示例如下:
# -*- coding: utf-8 -*-
"""
# @Time : 2022/11/26 22:16
# @Author : longrong.lang
# @FileName: login_page.py
# @Software: PyCharm
# @Blog :https://www.cnblogs.com/longronglang/
# @Motto:ABC(Always Be Coding)
"""
import time
from selenium.webdriver.common.by import By
class LoginPage:
"""
封裝元素定位及控制元件
"""
def __int__(self, driver):
self.driver = driver
# 開啟瀏覽器
def open(self, url):
self.driver.get(url)
# 使用者名稱元素定位
def user_name(self):
return self.driver.find_element(By.CSS_SELECTOR, "input[type='text']")
# 密碼元素定位
def pass_word(self):
return self.driver.find_element(By.CSS_SELECTOR, "input[type='password']")
# 登入元素定位
def login_btn(self):
return self.driver.find_element(By.CSS_SELECTOR, "button[name='submit']")
# 錯誤提示
def error_msg(self):
return self.driver.find_element(By.ID, "alert")
# 輸入使用者名稱
def send_username(self, username):
self.user_name().clear()
self.user_name().send_keys(username)
# 輸入密碼
def send_password(self, password):
self.pass_word().clear()
self.pass_word().send_keys(password)
# 點選登入
def click_login(self):
self.login_btn().click()
# 獲取錯誤提示
def get_errorMsg(self):
time.sleep(1)
return self.error_msg().text
# 退出瀏覽器
def quit(self):
self.driver.quit()
這裡我只對使用者名稱和密碼輸入框進行了封裝,有興趣的同學也可以接著進行全部元素操作封裝。
操作層
我們再新建一個類login_action
,用於登入邏輯的封裝,供業務層呼叫,具體程式碼示例如下:
# -*- coding: utf-8 -*-
"""
# @Time : 2022/11/26 22:33
# @Author : longrong.lang
# @FileName: login_action.py
# @Software: PyCharm
# @Blog :https://www.cnblogs.com/longronglang/
# @Motto:ABC(Always Be Coding)
"""
import time
from pageobject.login_page import LoginPage
class LoginAction(LoginPage):
def login(self, username, password, expected):
self.open("http://localhost:8080/login")
self.send_username(username)
self.send_password(password)
self.click_login()
time.sleep(1)
msg = self.get_errorMsg()
assert msg == expected
self.quit()
業務層
最後我們新建一個類test_login
,用於業務層的封裝,具體程式碼示例如下:
# -*- coding: utf-8 -*-
"""
# @Time : 2022/11/26 22:43
# @Author : longrong.lang
# @FileName: test_login.py
# @Software: PyCharm
# @Blog :https://www.cnblogs.com/longronglang/
# @Motto:ABC(Always Be Coding)
"""
from pageobject.login_action import LoginAction
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager
class TestLogin(LoginAction):
def test_login(self):
self.driver = webdriver.Chrome(ChromeDriverManager().install())
self.driver.maximize_window()
self.driver.implicitly_wait(5)
self.login("username", "pwd", "登入失敗!")
小結
雖然該實現方法看上去複雜多了,但其中的設計好處是不同層關心不同的問題。頁面物件只關心元素的定位,測試用例只關心測試資料。
login_page
類中主要對登入頁面上元素進行封裝,使其成為具體的操作方法。如對使用者名稱、密碼框都封裝成方法,然後定義login(self, username, password, expected)
方法將單個元素操作組成一個完整的動作,包含輸入使用者名稱、密碼並點選登入按鈕等。
使用時將driver、username、pwd、expected
作為函式的入參,這樣的方法具有很強的可重用性。
最後使用test_login()
方法進行使用者操作行為,現在只關心用哪個瀏覽器、登入的使用者名稱和密碼是什麼,至少輸入框、按鈕是如何定位的,則不關心。即實現了不同層關心不同問題。如果再有定位元素變化,只需login_page
這個類維護即可,顯然方便了很多。