一、PO模型
1、PO介紹:page(頁面) object(物件)
在自動化中,Selenium 自動化測試中有一個名字經常被提及 PageObject (思想與物件導向的特徵相同),通常PO 模型可以大大提高測試用例的維護效率。
優點:
提交測試指令碼可讀性
減少程式碼重複
提高測試用例的可維護性,特別是針對UI變動頻繁的專案缺點:
結構複雜:基於流程進行了模組化的拆分結構:
- base(基類)
- page(頁面物件)
- scripts(業務層)
擴充套件:
loc 變數:型別為元組:*loc為解包
2、PageObject 設計模式
3、PO 的核心要素
在 PO 模式中抽離封裝整合一個 BasePage 類,該基類應該擁有一個只實現 webdriver 例項的屬性
每一個page 都繼承BasePage,通過 driver 來管理 page 中元素,將 page 中的操作封裝成一個個方法
TestCase 繼承 unittest.TestCase類,並依賴page類,從而實現相應的測試步驟
二、將Selenium程式碼封裝成PO模型
1、案例說明(簡單的登入測試用例)
(1)改造案例思路:
- 第一, 我們要分離測試物件(元素物件)和測試指令碼(用例指令碼),那麼我們分別建立兩個指令碼檔案,分別為:
LoginPage.py
用於定義頁面元素物件,每一個元素都封裝成元件(可以看做存放頁面元素物件的倉庫)TestCaseLogin.py
測試用例指令碼。
- 第二,抽取出公共方法定義在
base.py
檔案中,每個Page
類都要繼承這個base.py
檔案,也就是每Page
類都能使用base
類中的方法,來操作頁面中的元素,同時也可以在每個Page
類中定義自己獨有的方法,解決工作中的實際需求。 - 第三,設計實現思想,一切元素和元素的操作元件化定義在
Page
頁面,用例指令碼頁面,通過呼叫Page
中的元件物件,進行拼湊成一個登入指令碼。
(2)封裝公共操作在base類(base.py)
把一些公共的方法放到此類中,這個類將被PO物件繼承。
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.support.wait import WebDriverWait
class Base(object):
def __init__(self, driver):
self.driver = driver
# 查詢元素方法(提供:點選、輸入、獲取文字)使用
def base_find_element(self, loc, timeout=30, poll=0.5):
element, index = loc[:2], loc[-1]
try:
ele = WebDriverWait(
self.driver, timeout=timeout, poll_frequency=poll
).until(
lambda x: x.find_elements(*element)
)[index]
except (NoSuchElementException, TimeoutError, IndexError):
return False
else:
return ele
# 點選方法
def base_click(self, loc):
self.base_find_element(loc).click()
# 輸入方法
def base_input(self, loc, value):
el = self.base_find_element(loc)
# 清空
el.clear()
# 輸入
el.send_keys(value)
# 獲取文字方法
def base_get_text(self, loc):
return self.base_find_element(loc).text
# 獲取當前頁面地址
def base_get_current_url(self):
return self.driver.current_url
# 滑鼠懸停事件
def base_move_to_element(self, loc):
ActionChains(self.driver).move_to_element(loc).perform()
(3)每個頁面對應一個Page類(login_page.py)
定位元素的定位器和操作元素方法分離開,元素定位器全部放一起,然後每一個操作元素動作寫成一個方法。
from selenium.webdriver.common.by import By
from public.base import Base
login_username = (By.CLASS_NAME, 'el-input__inner', 1)
login_password = (By.CLASS_NAME, 'el-input__inner', 2)
login_btn = (By.CLASS_NAME, 'login__button', 0)
organ_name = (By.CSS_SELECTOR, '.role-names-button', 0)
logout_btn = (By.CLASS_NAME, 'logout', 0)
password_login_text = (By.CSS_SELECTOR, '.pwd-login', 0)
class LoginPage(object):
def __init__(self, driver):
self.element = Base(driver)
# 輸入手機號
def input_username(self, content):
self.element.base_input(login_username, content)
# 輸入密碼
def input_password(self, content):
self.element.base_input(login_password, content)
# 點選登入
def click_login_button(self):
self.element.base_click(login_btn)
# 獲取當前頁面url
def get_current_url(self):
return self.element.base_get_current_url()
# 點選機構名稱
def click_to_organ_name(self):
self.element.base_click(organ_name)
# 點選退出
def click_logout(self):
self.element.base_click(logout_btn)
# 獲取“密碼登入”文字
def get_password_login_text(self):
return self.element.base_get_text(password_login_text)
(4)封裝業務層(login_business.py)
import time
from Test.Page.login_page import *
class LoginBusiness(object):
def __init__(self, driver):
self.login_business = LoginPage(driver)
# 登入系統
def go_system(self, username, password):
self.login_business.input_username(username)
self.login_business.input_password(password)
self.login_business.click_login_button()
time.sleep(2)
return self.login_business.get_current_url()
# 退出成功
def logout(self):
self.login_business.click_to_organ_name()
self.login_business.click_logout()
return self.login_business.get_password_login_text()
(4)原登陸案例封裝完成程式碼
測試方法及測試類的執行都在此檔案中。
from Test.Case.base_case import LoginBaseCase
from public.get_log import LogInfo
from public.read_ini import get_value
from Test.Business.login_business import LoginBusiness
class TestLogin(LoginBaseCase, LogInfo):
""" 登入退出測試用例 """
@classmethod
def setUpClass(cls) -> None:
cls.case_forward()
LogInfo().log.info('TestLogin Cases Suite Start Running')
cls.login = LoginBusiness(cls.driver)
@LogInfo.get_error
def test_1(self):
""" 登入流程 """
self.log.info('TestCase1 Start Running')
# 獲取使用者名稱密碼
username = get_value(get_value('Base', 'Env'), 'username')
password = get_value(get_value('Base', 'Env'), 'password')
url = self.login.go_system(username, password)
text = "https://gssdev.haoshengy.com/pc_workbench/workbench/overview"
self.assertEqual(url, text, '當前頁面URL不正確--測試不通過')
def test_2(self):
""" 退出登入 """
self.log.info('TestCase2 Start Running')
text = self.login.logout()
self.assertEqual("密碼登入", text, '首頁密碼登入字樣錯誤--測試不通過')
三、資料驅動
1.1 什麼是資料驅動
資料驅動:是以資料來驅動整個測試用例的執行,也就是測試資料決定測試結果
1.2 資料驅動的特點
- 資料驅動本身不是一個工業級標磚的概念,因此在不同的公司都會有不同的解釋
- 可以把資料驅動理解成一種模式或者一種思想
- 資料驅動技術可以讓使用者把關注點放在測試資料的構建和維護上,而不是直接維護指令碼,可以利用同樣的過程對不同的資料輸入進行測試
- 資料驅動的實現要依賴引數化的技術
1.3 傳入資料的方式(測試資料的來源)
- 直接定義在測試指令碼中(簡單直觀,但程式碼和資料未實現真正的分離,不方便後期維護)
- 從檔案讀取資料,如json、Excel、xml、txt、等格式檔案
- 從資料庫中讀取資料
- 直接呼叫介面獲取資料來源
- 本地封裝一些生成資料的方法