五分鐘搞懂POM設計模式

2022-06-12 18:00:59

轉載請註明出處❤️

作者:IT小學生蔡坨坨

原文連結:五分鐘搞懂POM設計模式


大家好,我是IT小學生蔡坨坨。

今天,我們來聊聊Web UI自動化測試中的POM設計模式。

為什麼要用POM設計模式

前期,我們學會了使用Python+Selenium編寫Web UI自動化測試線性指令碼

線性指令碼(以快遞100網站登入舉慄):

import time

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

driver = webdriver.Chrome()

driver.get("https://sso.kuaidi100.com/sso/v2/authorize.do")
driver.maximize_window()
driver.find_element(By.ID, 'name').send_keys("***********")
driver.find_element(By.ID, 'password').send_keys("***********")
driver.find_element(By.ID, 'submit').click()
time.sleep(2)
text = driver.find_element(By.PARTIAL_LINK_TEXT, '首頁').text
assert text == '首頁'
driver.close()

使用以上程式碼,最基礎最簡單的Web UI 自動化測試就做起來了,但是,問題也隨之而來,線性指令碼的缺點也暴露出來了:

  • Web UI自動化測試,簡單來說,就是模擬人在瀏覽器上的操作,開啟瀏覽器-定位元素-操作元素-模擬頁面動作-斷言結果
  • 由於線性指令碼中的元素定位元素操作細節測試資料結果驗證(斷言)是捆綁在一起的,程式碼會顯得非常冗餘、可讀性差、不可複用、工作量大且可維護性差
  • 剛開始,少數的測試用例維護起來可能很容易,但隨著時間遷移、產品迭代、測試套件持續增長,指令碼也越來越臃腫,可能需要維護幾十個頁面,且很多頁面是公用的,元素的任何改變都會讓我們的指令碼變的繁瑣複雜、耗時易出錯。例如:十幾個用例中都用到了A元素,某一天A元素被前端改成了B元素,我們就需要去十幾個用到A元素的地方,將A元素修改為B元素
  • 如果可以把公共元素抽取出來,即使元素被前端修改,我們也只需更新元素的定位方式,而不用修改每條測試用例,無論多少用例用到該元素,都只需修改元素定位方式,重新獲取元素即可
  • 所以我們引入了PageObject這種解決方案,它可以幫我們解決設計上的問題,可以將testcase和page分層,形成一個非常好的結果

什麼是POM設計模式

  • POM:Page Object Model,頁面物件模型的簡稱

  • 2013年,由Martin Fowler提出了PageObject的觀點

  • 作者的觀點是一種封裝思想,旨在為每個待測頁面建立一個頁面物件,從而將繁瑣的定位元運算、操作細節封裝到這個頁面物件中,對外只提供必要的操作介面,在呼叫的時候只呼叫提供的介面,不用去呼叫操作細節,最終實現程式的高內聚低耦合,使程式模組的可重用性、移植性大大增強

  • 在這種模式下,對於應用程式中的每個頁面都應該有相應單獨的頁面類(例如:login_page、userinfo_page),類中應該包含此頁面上的元素物件操作這些元素物件所需要的方法

  • 再將流程所關聯的頁面作為物件,將物件串聯起來形成不同的業務流程,例如:在登入頁面完成登入操作後跳轉到使用者中心頁面進行個人資訊的修改

Selenium官方對PageObject的引入

歷史

PageObject六大原則

  • The public methods represent the services that the page offers

    用公共方法表示頁面提供的服務

    例如:登入頁面,有使用者名稱輸入框、密碼輸入框、登入按鈕,於是就可以用input_username()代表輸入使用者名稱、用input_password()代表輸入密碼、用click_submit()代表點選登入按鈕

  • Try not to expose the internals of the page

    儘量不要暴露頁面的內部資訊

    將操作細節封裝成方法,對外只提供對應的方法供呼叫

  • Generally don’t make assertions

    一般不使用斷言

    斷言要和Page程式碼分開,不要將斷言寫在PageObject層

  • Methods return other PageObjects

    方法返回其他PageObjects

    例如:首頁有個方法是點選登入圖示跳轉到登入頁面,因此這個方法應該返回login_page

  • Need not represent an entire page

    不需要表示整個頁面

    不需要對頁面中的每一個元素進行建模,只需要關注我們需要用到的元素。例如:登入頁面除了賬號密碼登入,還有快捷登入、手機簡訊登入、掃碼登入等

  • Different results for the same action are modelled as different methods

    同一行為的不同結果可以用不同的方法來模擬

    例如:對一個頁面進行操作,可能出現正確的結果或者錯誤的結果,可以為這兩種不同的結果分別建立兩個不同的方法

POM框架

GitHub:關注微信公眾號 IT小學生蔡坨坨,回覆關鍵字 原始碼獲取

  • base:base_page,基礎類別,定義專案所需的基礎方法,對Selenium一些常用的api進行二次封裝,如:find_element、click、send_keys、screenshot、呼叫JavaScript指令碼的方法以及其他與瀏覽器相關的操作

    為什麼要有基礎類別?

    • 由於每個頁面都會頻繁使用這些方法,若單純使用Selenium原始api,可能遇到一些問題,例如:某個按鈕未載入完成,但已觸發了點選事件,導致元素定位不到而報錯。這時就可以對原始api進行二次封裝,如:加入等待時間、對異常進行捕獲並列印紀錄檔等,之後所有的PageObject都繼承BasePage類,後續只需要呼叫這些封裝好的方法,增強複用性
    • 假設以後不使用Selenium這個框架,就只需要修改BasePage中的方法,不用去修改具體的測試用例業務程式碼
  • pages:page_object,頁面物件層,也是PO的核心層,繼承BasePage,管理頁面元素以及操作元素的方法(將操作元素的動作寫成方法)

  • cases:測試用例層,用於管理測試用例,這裡會用到單元測試框架,如:Pytest、Unittest。

  • data:測試資料層,用於測試資料的管理,資料與指令碼分離,降低維護成本,提高可移植性,如:yml 檔案資料

  • config:組態檔層,存放整個專案需要用到的設定項,如:URL、資料庫資訊等

  • utils:CommonUtil,公共模組,將一些公共函數、方法以及通用操作進行封裝,如:紀錄檔模組、yaml 操作模組、時間模組等

  • run.py:批次執行測試用例的主程式,根據不同需求不同場景進行組裝,遵循框架的靈活性和擴充套件性

  • logs:紀錄檔模組,用於記錄和管理紀錄檔,針對不同情況,設定不同的紀錄檔級別,方便定位問題

  • reports:測試報告層,用於測試報告的生成和管理,如:基於 Allure 生成的客製化化報告