Selenium4+Python3系列(十)

2022-11-27 21:01:39

前言

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這個類維護即可,顯然方便了很多。