引言
前面一直在講接口自動化測試框架與案例分享,很少講Selenium這個Web自動化測試神器。它主要用來做UI自動化測試,大家都知道UI自動化測試成本相當高,一般的Web自動化測試我是一直不建議做的。
雖然不推薦,但是這里有一個設計思想是不錯的——PO設計模式。
PO設計模式
PO設計模式,英文名稱:Page Object Model。PO設計模式是Selenium自動化測試中最佳的設計方式之一。相比傳統設計中:頁面定位元素→輸入數據→操作元素→斷言結果,會有以下問題:
1、易用性差:雜亂無章的定位元素方法,例如:find_element;
2、擴展性不好:用例孤立,無法擴展;
3、復用性差:無公共方法,很難服用;
4、可維護性差:一旦元素變化或測試步驟變化,需要維護大量代碼和用例;
針對上面一些弊端,做了一些優化:
POM設計模式,將頁面定位和業務操作分開,將元素定位和測試方法分離,從而提高代碼的維護性。而傳統的POM是元素定位和測試方法放在一起,如下圖:

這樣做的優勢:
1、頁面元素定位和業務操作方法分離,使得代碼更加清晰,減少冗余代碼;
2、測試方法單獨抽離,這樣提高用例的可讀性;
3、針對ui變化頻繁的項目和測試步驟的變化,提高了測試用例的維護性;
一條測試用例可能需要多個步驟操作元素,將每一個步驟單獨封裝成一個方法,在執行測試用例時調用封裝好的方法進行操作。PO模式可以把一個頁面分為三個層級,對象庫層、操作層、業務層。
PO設計模式核心組件
畫一個操作如下:

首先抽象封裝一個BasePage類,這個基類擁有Webdriver實例的屬性,將頁面分成一個個Page,每一個Page繼承基類BasePage,可以通過driver來管理每一個Page中的元素,
在Page中將定位元素的操作封裝成一個一個方法。TestCase繼承unittest里面的TestCase類,並且依賴Page類,進行測試步驟的執行工作。
這樣以來,頁面元素一旦變化,只需要維護每一個Page中的方法,測試流程發生變化,只需要維護TestCase即可。
核心組件:
BasePage.py模塊:
class BasePage(object):
def __init__(self,driver):
self.driver = driver
pass
Page頁面模塊:
from SeleniumProject.PO.BasePage import BasePage
class LoginBase(BasePage):
# 定位元素,括號中是通過find_element來獲取元素的屬性
uname = ()
pwd = ()
def set_uname(self,uname):
name =self.driver.find_element(*LoginBase.uname)
name.send_keys("用戶名")
def set_pwd(self,pwd):
password = self.driver.find_element(*LoginBase.pwd)
password.send_keys("密碼")
pass
TestCase用例模塊:
from unittest import TestCase
import unittest
from selenium import webdriver
class Test_Login(TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.driver.get("https://cn.bing.com/")
# 測試步驟
def test_Login(self):
self.driver.get(self.base_url)
pass
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main()
PO模式簡單實例
現在根據PO設計模式思想,簡單實現一個需求:
打開瀏覽器,輸入url:https://www.baidu.com,在百度搜索文本框內輸入關鍵字:selenium,然后單擊:百度一下,進行搜索。
根據需求,設計步驟如下:
BasePage:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time : 2020/11/22 0022 16:07
# @Author : liudinglong
# @File : basepage.py
# @Description:
# @Question:
'''
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class BasePage(object):
def __init__(self,driver,url):
"""
@param driver:
@param base_url:
"""
self.dr = driver
self.base_url = url
# 定義私有方法,類對象和子類可以訪問
def _open(self,url):
self.dr.get(url)
self.dr.maximize_window()
# 定義open方法,調用_open方法
def open(self):
self._open(self.base_url)
def find_emelemt(self,*loc):
try:
WebDriverWait(self.dr,10).until(EC.visibility_of_all_elements_located(loc))
return self.dr.find_element(*loc)
except:
print("頁面中沒有%s元素"%(self.loc))
# 定義script()方法,用於執行JS腳本,比方上上傳文件啥的
def script(self, src):
self.dr.excute_script(src)
# 定義頁面跳轉方法,比方說有的頁面有frame嵌套
def switch_frame(self, loc):
return self.dr.switch_to_frame(loc)
# 重新定義send_keys()方法,為了保證搜索按鈕是否存在,還有有的輸入框中默認有值,要清空
def send_keys(self, loc, value, clear_first=True, click_first=True):
try:
# getattr方法相當於實現了self.loc
loc = getattr(self, "_%s" % loc)
# 是否存在搜索按鈕
if click_first:
self.find_emelemt(*loc).click()
# 清空搜索框中的值,並輸入需要搜索的值
if clear_first:
self.find_emelemt(*loc).clear()
self.find_emelemt(*loc).send_keys(value)
except:
print("頁面上未找到%s元素" % (self.loc))
SearchPage:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time : 2020/11/22 0022 18:38
# @Author : liudinglong
# @File : SearchPage.py
# @Description:
# @Question:
'''
from selenium.webdriver.common.by import By
from Common.basepage import BasePage
class SearchPage(BasePage):
# 定位元素
search_loc = (By.ID,"kw") #搜索框
btn_loc = (By.ID,"su") #搜索按鈕
# 重寫父類的open()方法
def open(self):
self._open(self.base_url)
def search_content(self,content):
# 調用父類的find_emelemt,然后將本類的參數傳入
content1 = self.find_emelemt(*self.search_loc)
content1.send_keys(content)
def btn_click(self):
btn1 = self.find_emelemt(*self.btn_loc)
btn1.click()
TestCase:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
# @Time : 2020/11/22 0022 18:40
# @Author : liudinglong
# @File : test_001.py
# @Description:
# @Question:
'''
from unittest import TestCase
import unittest
from selenium import webdriver
from time import sleep
from Page.searchpage import SearchPage
class CaseRun(TestCase):
def setUp(self):
self.driver = webdriver.Chrome()
self.url = "https://www.baidu.com"
sleep(3)
self.content = "selenium"
# 測試步驟
def test_search(self):
bing_page = SearchPage(self.driver,self.url)
bing_page.open()
bing_page.search_content(self.content)
try:
bing_page.btn_click()
sleep(3)
print("查詢成功")
except Exception as Error:
print(Error)
def tearDown(self):
self.driver.quit()
if __name__ == "__main__":
unittest.main()
三個核心組件完成,項目結構如下:

運行測試,生成報告如下:
C:\Users\Administrator\Desktop\Demo_PO C:\Users\Administrator\Desktop\Demo_PO\Report . Time Elapsed: 0:00:13.370322 Process finished with exit code 0

總結
這個Demo很簡單,主要意圖是幫助理解PO設計模式的思想,如果需要代碼,可以加入QQ群:696400122 ,我們這里主要是進行自動化測試和測試開發學習與溝通交流,如果其他意圖請繞行~
