當前位置:
首頁 > 知識 > 框架設計篇之3-什麼是POM框架內封裝基類和實現POM

框架設計篇之3-什麼是POM框架內封裝基類和實現POM

前面我們介紹了Python中的單元測試框架unittest,以後我們所有的測試類文件,都採用unittest來輔助我們進行debug和腳本開發。搞定了debug機制和確定了unittest來進行創建和管理我們的自動化測試腳本,接下來我們來考慮下,框架設計中一種很普遍的設計思想-POM(Page Object Model)

POM,中文字母意思是,頁面對象模型,POM是一種最近幾年非常流行的自動化測試模型,或者思想,POM不是一個框架,就是一個解決問題的思想。採用POM的目的,是為了解決前端中UI變化頻繁,從而造成測試自動化腳本維護的成本越來越大。下圖,形象描述了POM的好處。

從上圖看出,採取了POM設計思路和不採取的區別,左側把測試代碼和頁面元素都寫在一個類文件,如果需要更改頁面,那麼就要修改頁面元素定位,從而要修改這個類中測試代碼,這個看起來和混亂。右側,採取POM後,主要的區別就是,把頁面元素和業務邏輯和測試腳本分離出來到兩個不同類文件。ClassA只寫頁面元素定位,和業務邏輯代碼操作的封裝,ClassB只寫測試腳本,不關心如何元素定位,只寫調用ClassA的代碼去覆蓋不同的測試場景。如果前端頁面發生變化,只需要修改ClassA的元素定位,而不需要去修改ClassB中的測試腳本代碼。

POM主要有以下優點:

1. 把web ui對象倉庫從測試腳本分離,業務代碼和測試腳本分離。

2. 每一個頁面對應一個頁面類,頁面的元素寫到這個頁面類中。

3. 頁面類主要包括該頁面的元素定位,和和這些元素相關的業務操作代碼封裝的方法。

4. 代碼復用,從而減少測試腳本代碼量。

5. 層次清晰,同時支持多個編寫自動化腳本開發,例如每個人寫哪幾個頁面,不影響他人。

6. 建議頁面類和業務邏輯方法都給一個有意義的名稱,方便他人快速編寫腳本和維護腳本。

前面文章,我們實現了框架的一部分功能,包括日誌類和瀏覽器引擎類的封裝,今天我們繼續封裝一個基類和介紹如何實現POM。關於基類,是這樣定義的:把一些常見的頁面操作的selenium封裝到base_page.py這個類文件,以後每個POM中的頁面類,都繼承這個基類,這樣每個頁面類都有基類的方法,這個我們會在這篇文章實現。

1. 在實現封裝基類里,我們實現了元素八大方式的定位和截圖類封裝。具體項目層級結構如下圖。

2. 基類base_page.py的具體實現代碼,這裡就封裝了幾個常用方法,其他方法,你自己去練習封裝下。

代碼:

# coding=utf-8

import time

import os.path

from framework.logger import Logger

# create a logger instance

logger = Logger(logger="BasePage").getlog()

class BasePage(object):

"""

定義一個頁面基類,讓所有頁面都繼承這個類,封裝一些常用的頁面操作方法到這個類

"""

def __init__(self, driver):

self.driver = driver

# quit browser and end testing

def quit_browser(self):

# 瀏覽器前進操作

def forward(self):

logger.info("Click forward on current page.")

# 瀏覽器後退操作

def back(self):

logger.info("Click back on current page.")

# 隱式等待

def wait(self, seconds):

logger.info("wait for %d seconds." % seconds)

# 點擊關閉當前窗口

def close(self):

try:

logger.info("Closing and quit the browser.")

except NameError as e:

logger.error("Failed to quit the browser with %s" % e)

# 保存圖片

def get_windows_img(self):

"""

在這裡我們把file_path這個參數寫死,直接保存到我們項目根目錄的一個文件夾.Screenshots下

"""

file_path = os.path.dirname(os.path.abspath(".")) + "/screenshots/"

rq = time.strftime("%Y%m%d%H%M", time.localtime(time.time()))

screen_name = file_path + rq + ".png"

try:

logger.info("Had take screenshot and save to folder : /screenshots")

except NameError as e:

logger.error("Failed to take screenshot! %s" % e)

self.get_windows_img()

# 定位元素方法

def find_element(self, selector):

"""

這個地方為什麼是根據=>來切割字元串,請看頁面里定位元素的方法

submit_btn = "id=>su"

login_lnk = "xpath => //*[@id="u1"]/a[7]" # 百度首頁登錄鏈接定位

如果採用等號,結果很多xpath表達式中包含一個=,這樣會造成切割不準確,影響元素定位

:param selector:

:return: element

"""

element = ""

if "=>" not in selector:

selector_by = selector.split("=>")[0]

selector_value = selector.split("=>")[1]

if selector_by == "i" or selector_by == "id":

try:

logger.info("Had find the element " %s " successful "

"by %s via value: %s " % (element.text, selector_by, selector_value))

except NoSuchElementException as e:

logger.error("NoSuchElementException: %s" % e)

self.get_windows_img() # take screenshot

elif selector_by == "n" or selector_by == "name":

elif selector_by == "c" or selector_by == "class_name":

elif selector_by == "l" or selector_by == "link_text":

elif selector_by == "p" or selector_by == "partial_link_text":

elif selector_by == "t" or selector_by == "tag_name":

elif selector_by == "x" or selector_by == "xpath":

try:

logger.info("Had find the element " %s " successful "

"by %s via value: %s " % (element.text, selector_by, selector_value))

except NoSuchElementException as e:

logger.error("NoSuchElementException: %s" % e)

self.get_windows_img()

elif selector_by == "s" or selector_by == "selector_selector":

else:

raise NameError("Please enter a valid type of targeting elements.")

return element

# 輸入

def type(self, selector, text):

el = self.find_element(selector)

el.clear()

try:

el.send_keys(text)

logger.info("Had type " %s " in inputBox" % text)

except NameError as e:

logger.error("Failed to type in input box with %s" % e)

self.get_windows_img()

# 清除文本框

def clear(self, selector):

el = self.find_element(selector)

try:

el.clear()

logger.info("Clear text in input box before typing.")

except NameError as e:

logger.error("Failed to clear in input box with %s" % e)

self.get_windows_img()

# 點擊元素

def click(self, selector):

el = self.find_element(selector)

try:

el.click()

logger.info("The element " %s " was clicked." % el.text)

except NameError as e:

logger.error("Failed to click the element with %s" % e)

# 或者網頁標題

def get_page_title(self):

@staticmethod

def sleep(seconds):

time.sleep(seconds)

logger.info("Sleep for %d seconds" % seconds)

3. 頁面對象中,百度主頁的元素定位和簡單的操作函數,頁面類主要是元素定位和頁面操作寫成函數,供測試類調用。

baidu_homepage.py

# coding=utf-8

from framework.base_page import BasePage

class HomePage(BasePage):

input_box = "id=>kw"

search_submit_btn = "xpath=>//*[@id="su"]"

def type_search(self, text):

self.type(self.input_box, text)

def send_submit_btn(self):

self.click(self.search_submit_btn)

這裡注意下元素定位寫法,=>和base_page.py中find_element()方法元素定位切割有關係,網上有些人寫根據逗號切割或者等號切割,在實際使用xpath定位,發現單獨逗號或者單獨等號切割都不精確,造成元素定位失敗。

4. 測試類的寫法舉例

代碼

# coding=utf-8

import time

import unittest

from framework.browser_engine import BrowserEngine

from pageobjects.baidu_homepage import HomePage

class BaiduSearch(unittest.TestCase):

def setUp(self):

"""

測試固件的setUp()的代碼,主要是測試的前提準備工作

:return:

"""

browse = BrowserEngine(self)

self.driver = browse.open_browser(self)

def tearDown(self):

"""

測試結束後的操作,這裡基本上都是關閉瀏覽器

:return:

"""

def test_baidu_search(self):

"""

這裡一定要test開頭,把測試邏輯代碼封裝到一個test開頭的方法里。

:return:

"""

homepage = HomePage(self.driver)

homepage.type_search("selenium") # 調用頁面對象中的方法

homepage.send_submit_btn() #調用頁面對象類中的點擊搜索按鈕方法

time.sleep(2)

homepage.get_windows_img() # 調用基類截圖方法

try:

assert "selenium" in homepage.get_page_title() # 調用頁面對象繼承基類中的獲取頁面標題方法

print ("Test Pass.")

except Exception as e:

print ("Test Fail.", format(e))

if __name__ == "__main__":

unittest.main()

homepage = HomePage(self.driver)

上面這行代碼要注意,意思是,到一個頁面,第一件事情是初始化這個頁面的一個頁面對象實例。注意,一定要帶self.driver,不然會報錯,這個self.driver,可以這樣理解:在當前測試類裡面,self.driver是來自瀏覽器引擎類中方法得到的,在初始化一個頁面對象的時候,也把這個來自瀏覽器引擎類的driver給賦值給當前的頁面對象,這樣,才能執行頁面對象或者基類裡面的相關driver方法。寫多了selenium的自動化腳本,你會明白,最重要的是保持前後driver的唯一性。

測試結果:會在logs文件夾生成一個日誌文件,也會在screenshots文件夾生成一個png圖片。

歡迎關注凱哥公眾號:凱哥Java


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 凱哥java 的精彩文章:

自動化框架設計篇之2-簡單介紹unittest單元測試框架
Selenium框架設計篇之1什麼是自動化測試框架
中級篇之9-把截圖類方法封裝到前面的BasePage.py
框架設計篇之6-一個類文件多個測試方法情況下測試固件的寫法

TAG:凱哥java |

您可能感興趣

58集團RPC框架SCF的設計與實踐
揭秘RPC框架-實現原理
RPC框架的原理
終章!如何從零開始搭建MMOARPG系統框架?
如何設計一個類似Dubbo的RPC框架
TLAC框架的國際綁架
NET主流ORM框架分析
RPC框架的可靠性設計
從零學習Spring MVC框架「環境搭建和MVC架構」
通用視覺SLAM框架OpenVSLAM
AMD X570板框架曝光,ITX穩了
為Web藝術家創造的PHP框架
ICO監管框架
API Star:一個 Python 3 的 API 框架
揭秘RPC框架-核心組件
基於PyTorch的GAN框架TorchGAN:用架構級API輕鬆定製GAN項目
YOYOW-WeCenter框架發布,建站上鏈YOYOW更便捷了
100多個DL框架、AI庫、ML庫、NLP庫、CV庫匯總,建議收藏!
DeepMind 推出分散式訓練框架 IMPALA,開啟智能體訓練新時代
Unreal 4.19正式發布,為iOS、安卓提供統一AR項目框架