1. PO 設(shè)計(jì)模式簡介
什么是 PO 模式?
PO(PageObject)設(shè)計(jì)模式將某個頁面的所有元素對象定位和對元素對象的操作封裝成一個 Page 類,并以頁面為單位來寫測試用例,實(shí)現(xiàn)頁面對象和測試用例的分離。
PO 模式的設(shè)計(jì)思想與面向?qū)ο笙嗨?,能讓測試代碼變得可讀性更好,可維護(hù)性高,復(fù)用性高。
PO 模式可以把一個頁面分為三個層級:對象庫層、操作層、業(yè)務(wù)層。
對象庫層:封裝定位元素的方法。
操作層:封裝對元素的操作。
業(yè)務(wù)層:將一個或多個操作組合起來完成一個業(yè)務(wù)功能。
一條測試用例可能需要多個步驟操作元素,將每一個步驟單獨(dú)封裝成一個方法,在執(zhí)行測試用例時調(diào)用封裝好的方法進(jìn)行操作。
PO 模式的優(yōu)點(diǎn)
通過頁面分層,將測試代碼和被測試頁面的頁面元素及其操作方法進(jìn)行分離,降低代碼冗余。
頁面對象與用例分離,業(yè)務(wù)代碼與測試代碼分離,降低耦合性。
不同層級分屬不同用途,降低維護(hù)成本。
代碼可閱讀性增強(qiáng),整體流程更為清晰。
2. 工程結(jié)構(gòu)簡介
工程結(jié)構(gòu)
整個測試框架分為四層,通過分層的方式,測試代碼更容易理解,維護(hù)起來較為方便。
第一層是“測試工具層”:
util 包:用于實(shí)現(xiàn)測試過程中調(diào)用的工具類方法,例如讀取配置文件、頁面元素的操作方法、操作Excel文件等。
conf 包:配置文件及全局變量。
test_data 目錄:Excel 數(shù)據(jù)文件,包含測試數(shù)據(jù)輸入、測試結(jié)果輸出。
log 目錄:日志輸出文件。
screenshot_path 目錄:異常截圖保存目錄。
第二層是“服務(wù)層”,相當(dāng)于對測試對象的一個業(yè)務(wù)封裝。對于接口測試,是對遠(yuǎn)程方法的一個實(shí)現(xiàn);對于頁面測試,是對頁面元素或操作的一個封裝。
page 包:對象庫層及操作層,將所有頁面的元素對象定位及其操作分別封裝成一個類。
第三層是“測試用例邏輯層”,該層主要是將服務(wù)層封裝好的各個業(yè)務(wù)對象,組織成測試邏輯,進(jìn)行校驗(yàn)。
action 包:組裝單個用例的流程。
business_process 包:基于業(yè)務(wù)層和測試數(shù)據(jù)文件,執(zhí)行測試用例集合。
test_data 目錄:Excel 數(shù)據(jù)文件,包含測試數(shù)據(jù)輸入、測試結(jié)果輸出?!?br> 第四層是“測試場景層”,將測試用例組織成測試場景,實(shí)現(xiàn)各種級別 cases 的管理、冒煙,回歸等測試場景?!?/p>
main.py:本 PO 框架的運(yùn)行主入口。
框架特點(diǎn)
通過配置文件,實(shí)現(xiàn)頁面元素定位方式和測試代碼的分離。
使用 PO 模式,封裝了網(wǎng)頁中的頁面元素,方便測試代碼調(diào)用,也實(shí)現(xiàn)了一處維護(hù)全局生效的目標(biāo)。
在 excel 文件中定義多組測試數(shù)據(jù),每個登錄用戶都一一對應(yīng)一個存放聯(lián)系人數(shù)據(jù)的 sheet,測試框架可自動調(diào)用測試數(shù)據(jù)完成數(shù)據(jù)驅(qū)動測試。
實(shí)現(xiàn)了測試執(zhí)行過程中的日志記錄功能,可以通過日志文件分析測試腳本執(zhí)行的情況。
在 excel 數(shù)據(jù)文件中,通過設(shè)定“測試數(shù)據(jù)是否執(zhí)行”列的內(nèi)容為 y 或 n,自定義選擇測試數(shù)據(jù),測試執(zhí)行結(jié)束后會在"測試結(jié)果列"中顯示測試執(zhí)行的時間和結(jié)果,方便測試人員查看。
3. 工程代碼示例
page 包
對象庫層及操作層,將所有頁面的元素對象定位及其操作分別封裝成一個類。
login_page.py
from conf.global_var import *
from util.ini_parser import IniParser
from util.find_element_util import *
# 登錄頁面元素定位及操作
class LoginPage:
def __init__(self, driver):
self.driver = driver
# 初始化跳轉(zhuǎn)登錄頁面
self.driver.get(LOGIN_URL)
# 初始化指定ini配置文件及指定分組
self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_loginPage")
# 獲取frame元素對象
def get_frame_obj(self):
locate_method, locate_exp = self.cf.get_value("loginPage.frame").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 切換frame
def switch_frame(self):
self.driver.switch_to.frame(self.get_frame_obj())
# 獲取用戶名輸入框元素對象
def get_username_input_obj(self):
locate_method, locate_exp = self.cf.get_value("loginPage.username").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 清空用戶名輸入框操作
def clear_username(self):
self.get_username_input_obj().clear()
# 輸入用戶名操作
def input_username(self, value):
self.get_username_input_obj().send_keys(value)
# 獲取密碼輸入框元素對象
def get_pwd_input_obj(self):
locate_method, locate_exp = self.cf.get_value("loginPage.password").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 輸入密碼操作
def input_pwd(self, value):
self.get_pwd_input_obj().send_keys(value)
# 獲取登錄按鈕對象
def get_login_buttion_obj(self):
locate_method, locate_exp = self.cf.get_value("loginPage.loginbutton").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 點(diǎn)擊登錄按鈕操作
def click_login_button(self):
self.get_login_buttion_obj().click()
?home_page.py
from conf.global_var import *
from util.ini_parser import IniParser
from util.find_element_util import *
# 登錄后主頁元素定位及操作
class HomePage:
def __init__(self, driver):
self.driver = driver
# 初始化指定ini配置文件及指定分組
self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_homePage")
# 獲取“通訊錄”按鈕對象
def get_contact_button_obj(self):
locate_method, locate_exp = self.cf.get_value("homePage.addressLink").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 點(diǎn)擊“通訊錄”按鈕
def click_contact_button(self):
self.get_contact_button_obj().click()
contact_page.py
from conf.global_var import *
from util.ini_parser import IniParser
from util.find_element_util import *
# 通訊錄頁面元素定位及操作
class ContactPage:
def __init__(self, driver):
self.driver = driver
# 初始化指定ini配置文件及指定分組
self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_contactPersonPage")
# 獲取新建聯(lián)系人按鈕對象
def get_contact_create_button_obj(self):
locate_method, locate_exp = self.cf.get_value("contactPersonPage.createButton").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 點(diǎn)擊新建聯(lián)系人按鈕
def click_contact_creat_button(self):
self.get_contact_create_button_obj().click()
# 獲取姓名輸入框?qū)ο? def get_name_input_obj(self):
locate_method, locate_exp = self.cf.get_value("contactPersonPage.name").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 輸入姓名操作
def input_name(self, value):
self.get_name_input_obj().send_keys(value)
# 獲取郵箱輸入框?qū)ο? def get_email_input_obj(self):
locate_method, locate_exp = self.cf.get_value("contactPersonPage.email").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 輸入郵箱操作
def input_email(self, value):
self.get_email_input_obj().send_keys(value)
# 獲取星標(biāo)聯(lián)系人單選框?qū)ο? def get_star_button_obj(self):
locate_method, locate_exp = self.cf.get_value("contactPersonPage.starContacts").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 點(diǎn)擊星標(biāo)聯(lián)系人操作
def click_star_button(self):
self.get_star_button_obj().click()
# 獲取手機(jī)輸入框?qū)ο? def get_phone_input_obj(self):
locate_method, locate_exp = self.cf.get_value("contactPersonPage.phone").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 輸入郵箱操作
def input_phone(self, value):
self.get_phone_input_obj().send_keys(value)
# 獲取備注輸入框?qū)ο? def get_remark_input_obj(self):
locate_method, locate_exp = self.cf.get_value("contactPersonPage.otherinfo").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 輸入郵箱操作
def input_remark(self, value):
self.get_remark_input_obj().send_keys(value)
# 獲取確定按鈕對象
def get_confirm_button_obj(self):
locate_method, locate_exp = self.cf.get_value("contactPersonPage.confirmButton").split(">")
return find_element(self.driver, locate_method, locate_exp)
# 點(diǎn)擊星標(biāo)聯(lián)系人操作
def click_confirm_button(self):
self.get_confirm_button_obj().click()
action 包
業(yè)務(wù)層,將一個或多個操作組合起來完成一個業(yè)務(wù)功能。
case_action.py
from selenium import webdriver
import traceback
import time
from page.contact_page import ContactPage
from page.home_page import HomePage
from page.login_page import LoginPage
from conf.global_var import *
from util.log_util import *
# 初始化瀏覽器
def init_browser(browser_name):
if browser_name.lower() == "chrome":
driver = webdriver.Chrome(CHROME_DRIVER)
elif browser_name.lower() == "firefox":
driver = webdriver.Firefox(FIREFOX_DRIVER)
elif browser_name.lower() == "ie":
driver = webdriver.Ie(IE_DRIVER)
else:
return "Error browser name!"
return driver
def assert_word(driver, text):
assert text in driver.page_source
# 登錄流程封裝
def login(driver, username, pwd, assert_text):
login_page = LoginPage(driver)
login_page.switch_frame()
login_page.clear_username()
login_page.input_username(username)
login_page.input_pwd(pwd)
login_page.click_login_button()
time.sleep(1)
assert_word(driver, assert_text)
# 添加聯(lián)系人流程封裝
def add_contact(driver, name, email, phone, is_star, remark, assert_text):
home_page = HomePage(driver)
home_page.click_contact_button()
contact_page = ContactPage(driver)
contact_page.click_contact_creat_button()
contact_page.input_name(name)
contact_page.input_email(email)
contact_page.input_phone(phone)
contact_page.input_remark(remark)
if is_star == "是":
contact_page.click_star_button()
contact_page.click_confirm_button()
time.sleep(2)
assert_word(driver, assert_text)
def quit(driver):
driver.quit()
if __name__ == "__main__":
driver = init_browser("chrome")
login(driver, "zhangjun252950418", "zhangjun123", "退出")
add_contact(driver, "鐵蛋", "asfhi@123.com", "12222222222", "是", "這是備注", "鐵蛋")
# quit(driver)
business_process 包
基于業(yè)務(wù)層和測試文件,實(shí)現(xiàn)數(shù)據(jù)驅(qū)動的測試執(zhí)行腳本。
batch_login_process.py
from action.case_action import *
from util.excel_util import *
from conf.global_var import *
from util.datetime_util import *
from util.screenshot import take_screenshot
# 封裝測試數(shù)據(jù)文件中用例的執(zhí)行邏輯
# 測試數(shù)據(jù)文件中的每個登錄賬號
def batch_login(test_data_file, browser_name, account_sheet_name):
excel = Excel(test_data_file)
# 獲取登錄賬號sheet頁數(shù)據(jù)
excel.change_sheet(account_sheet_name)
account_all_data = excel.get_all_row_data()
account_headline_data = account_all_data[0]
for account_row_data in account_all_data[1:]:
# 執(zhí)行登錄用例
account_row_data[ACCOUNT_TEST_TIME_COL] = get_english_datetime()
if account_row_data[ACCOUNT_IS_EXECUTE_COL].lower() == "n":
continue
# 初始化瀏覽器
driver = init_browser(browser_name)
try:
# 默認(rèn)以"退出"作為斷言關(guān)鍵字
login(driver, account_row_data[ACCOUNT_USERNAME_COL], account_row_data[ACCOUNT_PWD_COL], "退出")
info("登錄成功【用戶名:{}, 密碼:{}, 斷言關(guān)鍵字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
account_row_data[ACCOUNT_PWD_COL], "退出"))
account_row_data[ACCOUNT_TEST_RESULT_COL] = "pass"
except:
error("登錄失敗【用戶名:{}, 密碼:{}, 斷言關(guān)鍵字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
account_row_data[ACCOUNT_PWD_COL], "退出"))
account_row_data[ACCOUNT_TEST_RESULT_COL] = "fail"
account_row_data[ACCOUNT_TEST_EXCEPTION_INFO_COL] = traceback.format_exc()
account_row_data[ACCOUNT_SCREENSHOT_COL] = take_screenshot(driver)
# 寫入登錄用例的測試結(jié)果
excel.change_sheet("測試結(jié)果")
excel.write_row_data(account_headline_data, "red")
excel.write_row_data(account_row_data)
excel.save()
# 切換另一個賬號時需先關(guān)閉瀏覽器,否則會自動登錄
driver.quit()
if __name__ == "__main__":
batch_login(TEST_DATA_FILE_PATH, "chrome", "126賬號")
batch_login_and_add_contact_process.py
from action.case_action import *
from util.excel_util import *
from conf.global_var import *
from util.datetime_util import *
from util.screenshot import take_screenshot
# 封裝測試數(shù)據(jù)文件中用例的執(zhí)行邏輯
# 測試數(shù)據(jù)文件中每個登錄賬號下,添加所有聯(lián)系人數(shù)據(jù)
def batch_login_and_add_contact(test_data_file, browser_name, account_sheet_name):
excel = Excel(test_data_file)
# 獲取登錄賬號sheet頁數(shù)據(jù)
excel.change_sheet(account_sheet_name)
account_all_data = excel.get_all_row_data()
account_headline_data = account_all_data[0]
for account_row_data in account_all_data[1:]:
# 執(zhí)行登錄用例
account_row_data[ACCOUNT_TEST_TIME_COL] = get_english_datetime()
if account_row_data[ACCOUNT_IS_EXECUTE_COL].lower() == "n":
continue
# 初始化瀏覽器
driver = init_browser(browser_name)
# 獲取聯(lián)系人數(shù)據(jù)sheet
contact_data_sheet = account_row_data[ACCOUNT_DATA_SHEET_COL]
try:
# 默認(rèn)以"退出"作為斷言關(guān)鍵字
login(driver, account_row_data[ACCOUNT_USERNAME_COL], account_row_data[ACCOUNT_PWD_COL], "退出")
info("登錄成功【用戶名:{}, 密碼:{}, 斷言關(guān)鍵字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
account_row_data[ACCOUNT_PWD_COL], "退出"))
account_row_data[ACCOUNT_TEST_RESULT_COL] = "pass"
except:
error("登錄失敗【用戶名:{}, 密碼:{}, 斷言關(guān)鍵字:{}】".format(account_row_data[ACCOUNT_USERNAME_COL],
account_row_data[ACCOUNT_PWD_COL], "退出"))
account_row_data[ACCOUNT_TEST_RESULT_COL] = "fail"
account_row_data[ACCOUNT_TEST_EXCEPTION_INFO_COL] = traceback.format_exc()
account_row_data[ACCOUNT_SCREENSHOT_COL] = take_screenshot(driver)
# 寫入登錄用例的測試結(jié)果
excel.change_sheet("測試結(jié)果")
excel.write_row_data(account_headline_data, "red")
excel.write_row_data(account_row_data)
excel.save()
# 執(zhí)行添加聯(lián)系人用例
excel.change_sheet(contact_data_sheet)
contact_all_data = excel.get_all_row_data()
contact_headline_data = contact_all_data[0]
# 在測試結(jié)果中,一個賬號下的聯(lián)系人數(shù)據(jù)標(biāo)題行僅寫一次
contact_headline_flag = True
for contact_row_data in contact_all_data[1:]:
if contact_row_data[CONTACT_IS_EXECUTE_COL].lower() == "n":
continue
contact_row_data[CONTACT_TEST_TIME_COL] = get_english_datetime()
try:
add_contact(driver, contact_row_data[CONTACT_NAME_COL], contact_row_data[CONTACT_EMAIL_COL],
contact_row_data[CONTACT_PHONE_COL], contact_row_data[CONTACT_IS_STAR_COL],
contact_row_data[CONTACT_REMARK_COL], contact_row_data[CONTACT_ASSERT_KEYWORD_COL])
info("添加聯(lián)系人成功【姓名:{}, 郵箱:{}, 手機(jī)號:{}, 是否星標(biāo)聯(lián)系人:{}, "
"備注:{}, 斷言關(guān)鍵字:{}】".format(contact_row_data[CONTACT_NAME_COL], contact_row_data[CONTACT_EMAIL_COL],
contact_row_data[CONTACT_PHONE_COL], contact_row_data[CONTACT_IS_STAR_COL],
contact_row_data[CONTACT_REMARK_COL], contact_row_data[CONTACT_ASSERT_KEYWORD_COL]))
contact_row_data[CONTACT_TEST_RESULT_COL] = "pass"
except:
error("添加聯(lián)系人失敗【姓名:{}, 郵箱:{}, 手機(jī)號:{}, 是否星標(biāo)聯(lián)系人:{}, "
"備注:{}, 斷言關(guān)鍵字:{}】".format(contact_row_data[CONTACT_NAME_COL], contact_row_data[CONTACT_EMAIL_COL],
contact_row_data[CONTACT_PHONE_COL], contact_row_data[CONTACT_IS_STAR_COL],
contact_row_data[CONTACT_REMARK_COL], contact_row_data[CONTACT_ASSERT_KEYWORD_COL]))
contact_row_data[CONTACT_TEST_RESULT_COL] = "fail"
contact_row_data[CONTACT_TEST_EXCEPTION_INFO_COL] = traceback.format_exc()
contact_row_data[CONTACT_SCREENSHOT_COL] = take_screenshot(driver)
# 寫入登錄用例的測試結(jié)果
excel.change_sheet("測試結(jié)果")
if contact_headline_flag:
excel.write_row_data(contact_headline_data, "red")
contact_headline_flag = False
excel.write_row_data(contact_row_data)
excel.save()
# 切換另一個賬號時需先關(guān)閉瀏覽器,否則會自動登錄
driver.quit()
if __name__ == "__main__":
batch_login_and_add_contact(TEST_DATA_FILE_PATH, "chrome", "126賬號")
util 包
用于實(shí)現(xiàn)測試過程中調(diào)用的工具類方法,例如讀取配置文件、頁面元素的操作方法、操作Excel文件等。
excel_util.py
(openpyxl 版本:3.0.4)
from openpyxl import load_workbook
from openpyxl.styles import PatternFill, Font, Side, Border
import os
class Excel:
def __init__(self, test_data_file_path):
# 文件格式校驗(yàn)
if not os.path.exists(test_data_file_path):
print("Excel工具類初始化失敗:【{}】文件不存在!".format(test_data_file_path))
return
if not test_data_file_path.endswith(".xlsx") or not test_data_file_path.endswith(".xlsx"):
print("Excel工具類初始化失?。骸緖}】文件非excel文件類型!".format(test_data_file_path))
return
# 打開指定excel文件
self.wb = load_workbook(test_data_file_path)
# 初始化默認(rèn)sheet
self.ws = self.wb.active
# 保存文件時使用的文件路徑
self.test_data_file_path = test_data_file_path
# 初始化紅、綠色,供樣式使用
self.color_dict = {"red": "FFFF3030", "green": "FF008B00"}
# 查看所有sheet名稱
def get_sheets(self):
return self.wb.sheetnames
# 根據(jù)sheet名稱切換sheet
def change_sheet(self, sheet_name):
if sheet_name not in self.get_sheets():
print("sheet切換失?。骸緖}】指定sheet名稱不存在!".format(sheet_name))
return
self.ws = self.wb.get_sheet_by_name(sheet_name)
# 返回當(dāng)前sheet的最大行號
def max_row_num(self):
return self.ws.max_row
# 返回當(dāng)前sheet的最大列號
def max_col_num(self):
return self.ws.max_column
# 獲取指定行數(shù)據(jù)(設(shè)定索引從0開始)
def get_one_row_data(self, row_no):
if row_no < 0 or row_no > self.max_row_num()-1:
print("輸入的行號【{}】有誤:需在0至最大行數(shù)之間!".format(row_no))
return
# API的索引從1開始
return [cell.value for cell in self.ws[row_no+1]]
# 獲取指定列數(shù)據(jù)
def get_one_col_data(self, col_no):
if col_no < 0 or col_no > self.max_col_num()-1:
print("輸入的列號【{}】有誤:需在0至最大列數(shù)之間!".format(col_no))
return
return [cell.value for cell in tuple(self.ws.columns)[col_no+1]]
# 獲取當(dāng)前sheet的所有行數(shù)據(jù)
def get_all_row_data(self):
result = []
# # API的索引從1開始
for row_data in self.ws[1:self.max_row_num()]:
result.append([cell.value if cell.value is not None else "" for cell in row_data])
return result
# 追加一行數(shù)據(jù)
def write_row_data(self, data, fill_color=None, font_color=None, border=True):
if not isinstance(data, (list, tuple)):
print("追加的數(shù)據(jù)類型有誤:需為列號或元組類型!【{}】".format(data))
return
self.ws.append(data)
# 添加字體顏色
if font_color:
if font_color in self.color_dict.keys():
font_color = self.color_dict[font_color]
# 需要設(shè)置的單元格長度應(yīng)與數(shù)據(jù)長度一致,否則默認(rèn)與之前行的長度一致
count = 0
for cell in self.ws[self.max_row_num()]:
if count > len(data) - 1:
break
# cell不為None,才能設(shè)置樣式
if cell:
if cell.value in ["pass", "成功"]:
cell.font = Font(color=self.color_dict["green"])
elif cell.value in ["fail", "失敗"]:
cell.font = Font(color=self.color_dict["red"])
else:
cell.font = Font(color=font_color)
count += 1
# 添加背景顏色
if fill_color:
if fill_color in self.color_dict.keys():
fill_color = self.color_dict[fill_color]
count = 0
for cell in self.ws[self.max_row_num()]:
if count > len(data) - 1:
break
if cell:
cell.fill = PatternFill(fill_type="solid", fgColor=fill_color)
count += 1
# 添加單元格邊框
if border:
bd = Side(style="thin", color="000000")
count = 0
for cell in self.ws[self.max_row_num()]:
if count > len(data) - 1:
break
if cell:
cell.border = Border(left=bd, right=bd, top=bd, bottom=bd)
count += 1
# 保存文件
def save(self):
self.wb.save(self.test_data_file_path)
if __name__ == "__main__":
from conf.global_var import *
excel = Excel(TEST_DATA_FILE_PATH)
excel.change_sheet("登錄1")
# print(excel.get_all_row_data())
excel.write_row_data((1,2,"嘻哈",None,"ddd"), "red", "green")
excel.save()
find_element_util.py
from selenium.webdriver.support.ui import WebDriverWait
# 顯式等待一個對象
def find_element(driver, locate_method, locate_exp):
# 顯式等待對象(最多等10秒,每0.2秒判斷一次等待的條件)
return WebDriverWait(driver, 10, 0.2).until(lambda x: x.find_element(locate_method, locate_exp))
# 顯式等待一組對象
def find_elements(driver, locate_method, locate_exp):
# 顯式等待對象(最多等10秒,每0.2秒判斷一次等待的條件)
return WebDriverWait(driver, 10, 0.2).until(lambda x: x.find_elements(locate_method, locate_exp))
ini_parser.py
import configparser
class IniParser:
# 初始化打開指定ini文件并指定編碼
def __init__(self, file_path, section):
self.cf = configparser.ConfigParser()
self.cf.read(file_path, encoding="utf-8")
self.section = section
# 獲取所有分組名稱
def get_sections(self):
return self.cf.sections()
# 獲取指定分組的所有鍵
def get_options(self):
return self.cf.options(self.section)
# 獲取指定分組的鍵值對
def get_items(self):
return self.cf.items(self.section)
# 獲取指定分組的指定鍵的值
def get_value(self, key):
return self.cf.get(self.section, key)
datetime_util.py
import time
# 返回中文格式的日期:xxxx年xx月xx日
def get_chinese_date():
year = time.localtime().tm_year
if len(str(year)) == 1:
year = "0" + str(year)
month = time.localtime().tm_mon
if len(str(month)) == 1:
month = "0" + str(month)
day = time.localtime().tm_mday
if len(str(day)) == 1:
day = "0" + str(day)
return "{}年{}月{}日".format(year, month, day)
# 返回英文格式的日期:xxxx/xx/xx
def get_english_date():
year = time.localtime().tm_year
if len(str(year)) == 1:
year = "0" + str(year)
month = time.localtime().tm_mon
if len(str(month)) == 1:
month = "0" + str(month)
day = time.localtime().tm_mday
if len(str(day)) == 1:
day = "0" + str(day)
return "{}/{}/{}".format(year, month, day)
# 返回中文格式的時間:xx時xx分xx秒
def get_chinese_time():
hour = time.localtime().tm_hour
if len(str(hour)) == 1:
hour = "0" + str(hour)
minute = time.localtime().tm_min
if len(str(minute)) == 1:
minute = "0" + str(minute)
second = time.localtime().tm_sec
if len(str(second)) == 1:
second = "0" + str(second)
return "{}時{}分{}秒".format(hour, minute, second)
# 返回英文格式的時間:xx:xx:xx
def get_english_time():
hour = time.localtime().tm_hour
if len(str(hour)) == 1:
hour = "0" + str(hour)
minute = time.localtime().tm_min
if len(str(minute)) == 1:
minute = "0" + str(minute)
second = time.localtime().tm_sec
if len(str(second)) == 1:
second = "0" + str(second)
return "{}:{}:{}".format(hour, minute, second)
# 返回中文格式的日期時間
def get_chinese_datetime():
return get_chinese_date() + " " + get_chinese_time()
# 返回英文格式的日期時間
def get_english_datetime():
return get_english_date() + " " + get_english_time()
if __name__ == "__main__":
print(get_chinese_datetime())
print(get_english_datetime())
log_util.py
import logging
import logging.config
from conf.global_var import *
# 日志配置文件:多個logger,每個logger指定不同的handler
# handler:設(shè)定了日志輸出行的格式
# 以及設(shè)定寫日志到文件(是否回滾)?還是到屏幕
# 還定了打印日志的級別
logging.config.fileConfig(LOG_CONF_FILE_PATH)
logger = logging.getLogger("example01")
def debug(message):
logging.debug(message)
def info(message):
logging.info(message)
def warning(message):
logging.warning(message)
def error(message):
logging.error(message)
if __name__ == "__main__":
debug("hi")
info("gloryroad")
warning("hello")
error("這是一個error日志")
screenshot.py
import traceback
import os
from util.datetime_util import *
from conf.global_var import *
# 截圖函數(shù)
def take_screenshot(driver):
# 創(chuàng)建當(dāng)前日期目錄
dir = os.path.join(SCREENSHOT_PATH, get_chinese_date())
if not os.path.exists(dir):
os.makedirs(dir)
# 以當(dāng)前時間為文件名
file_name = get_chinese_time()
file_path = os.path.join(dir, file_name+".png")
try:
driver.get_screenshot_as_file(file_path)
# 返回截圖文件的絕對路徑
return file_path
except:
print("截圖發(fā)生異?!緖}】".format(file_path))
traceback.print_exc()
return file_path
conf 包
配置文件及全局變量。
elements_repository.ini
[126mail_loginPage]
loginPage.frame=xpath>//iframe[contains(@id,'x-URS-iframe')]
loginPage.username=xpath>//input[@name='email']
loginPage.password=xpath>//input[@name='password']
loginPage.loginbutton=id>dologin
[126mail_homePage]
homePage.addressLink=xpath>//div[text()='通訊錄']
[126mail_contactPersonPage]
contactPersonPage.createButton=xpath>//span[text()='新建聯(lián)系人']
contactPersonPage.name=xpath>//a[@title='編輯詳細(xì)姓名']/preceding-sibling::div/input
contactPersonPage.email=xpath>//*[@id='iaddress_MAIL_wrap']//input
contactPersonPage.starContacts=xpath>//span[text()='設(shè)為星標(biāo)聯(lián)系人']/preceding-sibling::span/b
contactPersonPage.phone=xpath>//*[@id='iaddress_TEL_wrap']//dd//input
contactPersonPage.otherinfo=xpath>//textarea
contactPersonPage.confirmButton=xpath>//span[.='確 定']
global_var.py
import os
# 工程根路徑
PROJECT_ROOT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 元素定位方法的ini配置文件路徑
ELEMENT_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "conf", "elements_repository.ini")
# 驅(qū)動路徑
CHROME_DRIVER = "E:\\auto_test_driver\\chromedriver.exe"
IE_DRIVER = "E:\\auto_test_driver\\IEDriverServer.exe"
FIREFOX_DRIVER = "E:\\auto_test_driver\\geckodriver.exe"
# 測試使用的瀏覽器
BROWSER_NAME = "chrome"
# 登錄主頁
LOGIN_URL = "https://mail.126.com"
# 日志配置文件路徑
LOG_CONF_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "conf", "logger.conf")
# 測試用例文件路徑
TEST_DATA_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "test_data", "測試用例.xlsx")
# 截圖保存路徑
SCREENSHOT_PATH = os.path.join(PROJECT_ROOT_PATH, "screenshot_path")
# 單元測試報告輸出目錄
UNITTEST_REPORT_PATH = os.path.join(PROJECT_ROOT_PATH, "report")
# 登錄賬號sheet頁數(shù)據(jù)列號
ACCOUNT_USERNAME_COL = 1
ACCOUNT_PWD_COL = 2
ACCOUNT_DATA_SHEET_COL = 3
ACCOUNT_IS_EXECUTE_COL = 4
ACCOUNT_TEST_TIME_COL = 5
ACCOUNT_TEST_RESULT_COL = 6
ACCOUNT_TEST_EXCEPTION_INFO_COL = 7
ACCOUNT_SCREENSHOT_COL = 8
# 聯(lián)系人sheet頁數(shù)據(jù)列號
CONTACT_NAME_COL = 1
CONTACT_EMAIL_COL = 2
CONTACT_IS_STAR_COL = 3
CONTACT_PHONE_COL = 4
CONTACT_REMARK_COL = 5
CONTACT_ASSERT_KEYWORD_COL = 6
CONTACT_IS_EXECUTE_COL = 7
CONTACT_TEST_TIME_COL = 8
CONTACT_TEST_RESULT_COL = 9
CONTACT_TEST_EXCEPTION_INFO_COL = 10
CONTACT_SCREENSHOT_COL = 11
if __name__ == "__main__":
print(PROJECT_ROOT_PATH)
logger.conf
###############################################
[loggers]
keys=root,example01,example02
[logger_root]
level=DEBUG
handlers=hand01,hand02
[logger_example01]
handlers=hand01,hand02
qualname=example01
propagate=0
[logger_example02]
handlers=hand01,hand03
qualname=example02
propagate=0
###############################################
[handlers]
keys=hand01,hand02,hand03
[handler_hand01]
class=StreamHandler
level=INFO
formatter=form01
args=(sys.stderr,)
[handler_hand02]
class=FileHandler
level=DEBUG
formatter=form01
args=('.\\log\\126_mail_test.log', 'a')
[handler_hand03]
class=handlers.RotatingFileHandler
level=INFO
formatter=form01
args=('.\\log\\126_mail_test.log', 'a', 10*1024*1024, 5)
###############################################
[formatters]
keys=form01,form02
[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s
datefmt=%Y-%m-%d %H:%M:%S
[formatter_form02]
format=%(name)-12s: %(levelname)-8s %(message)s
datefmt=%Y-%m-%d %H:%M:%S
test_data 目錄
測試用例.xlsx:包含測試數(shù)據(jù)輸入、測試結(jié)果輸出
?
log 目錄
日志輸出文件:126_mail_test.log
...
...
2021-02-23 16:59:15 log_util.py[line:19] INFO 登錄成功【用戶名:zhangjun252950418, 密碼:zhangjun123, 斷言關(guān)鍵字:退出】
2021-02-23 16:59:20 log_util.py[line:19] INFO 添加聯(lián)系人成功【姓名:lily, 郵箱:lily@qq.com, 手機(jī)號:135xxxxxxx1, 是否星標(biāo)聯(lián)系人:是, 備注:常聯(lián)系人, 斷言關(guān)鍵字:lily@qq.com】
2021-02-23 16:59:24 log_util.py[line:27] ERROR 添加聯(lián)系人失敗【姓名:張三, 郵箱:zhangsan@qq.com, 手機(jī)號:158xxxxxxx3, 是否星標(biāo)聯(lián)系人:否, 備注:不常聯(lián)系人, 斷言關(guān)鍵字:zhangsan@qq.comxx】
2021-02-23 16:59:27 log_util.py[line:19] INFO 添加聯(lián)系人成功【姓名:李四, 郵箱:lisi@qq.com, 手機(jī)號:157xxxxxx9, 是否星標(biāo)聯(lián)系人:否, 備注:, 斷言關(guān)鍵字:李四】
...
...
screenshot_path 目錄
異常截圖保存目錄:
main.py
本 PO 框架的運(yùn)行主入口。
from business_process.batch_login import *
from business_process.batch_login_and_add_contact import *
from conf.global_var import *
# 示例組裝:冒煙測試
def smoke_test():
batch_login(TEST_DATA_FILE_PATH, "chrome", "126賬號")
# 示例組裝:全量測試
def full_test():
batch_login_and_add_contact(TEST_DATA_FILE_PATH, "chrome", "126賬號")
if __name__ == "__main__":
# smoke_test()
full_test()
總結(jié):
感謝每一個認(rèn)真閱讀我文章的人?。?!
作為一位過來人也是希望大家少走一些彎路,如果你不想再體驗(yàn)一次學(xué)習(xí)時找不到資料,沒人解答問題,堅(jiān)持幾天便放棄的感受的話,在這里我給大家分享一些自動化測試的學(xué)習(xí)資源,希望能給你前進(jìn)的路上帶來幫助
軟件測試面試文檔
我們學(xué)習(xí)必然是為了找到高薪的工作,下面這些面試題是來自阿里、騰訊、字節(jié)等一線互聯(lián)網(wǎng)大廠最新的面試資料,并且有字節(jié)大佬給出了權(quán)威的解答,刷完這一套面試資料相信大家都能找到滿意的工作。
?
文章來源:http://www.zghlxwxcb.cn/news/detail-767341.html
?文章來源地址http://www.zghlxwxcb.cn/news/detail-767341.html
到了這里,關(guān)于UI 自動化測試框架:PO模式+數(shù)據(jù)驅(qū)動的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!