国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)

這篇具有很好參考價(jià)值的文章主要介紹了UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

1. PO 設(shè)計(jì)模式簡(jiǎn)介

什么是 PO 模式?

PO(PageObject)設(shè)計(jì)模式將某個(gè)頁(yè)面的所有元素對(duì)象定位和對(duì)元素對(duì)象的操作封裝成一個(gè) Page 類(lèi),并以頁(yè)面為單位來(lái)寫(xiě)測(cè)試用例,實(shí)現(xiàn)頁(yè)面對(duì)象和測(cè)試用例的分離。

PO 模式的設(shè)計(jì)思想與面向?qū)ο笙嗨?,能讓測(cè)試代碼變得可讀性更好,可維護(hù)性高,復(fù)用性高。

UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)
PO 模式可以把一個(gè)頁(yè)面分為三個(gè)層級(jí):對(duì)象庫(kù)層、操作層、業(yè)務(wù)層。

  1. 對(duì)象庫(kù)層:封裝定位元素的方法。

  2. 操作層:封裝對(duì)元素的操作。

  3. 業(yè)務(wù)層:將一個(gè)或多個(gè)操作組合起來(lái)完成一個(gè)業(yè)務(wù)功能。

一條測(cè)試用例可能需要多個(gè)步驟操作元素,將每一個(gè)步驟單獨(dú)封裝成一個(gè)方法,在執(zhí)行測(cè)試用例時(shí)調(diào)用封裝好的方法進(jìn)行操作。

PO 模式的優(yōu)點(diǎn)

  • 通過(guò)頁(yè)面分層,將測(cè)試代碼和被測(cè)試頁(yè)面的頁(yè)面元素及其操作方法進(jìn)行分離,降低代碼冗余。

  • 頁(yè)面對(duì)象與用例分離,業(yè)務(wù)代碼與測(cè)試代碼分離,降低耦合性。

  • 不同層級(jí)分屬不同用途,降低維護(hù)成本。

  • 代碼可閱讀性增強(qiáng),整體流程更為清晰。

2. 工程結(jié)構(gòu)簡(jiǎn)介

工程結(jié)構(gòu)

整個(gè)測(cè)試框架分為四層,通過(guò)分層的方式,測(cè)試代碼更容易理解,維護(hù)起來(lái)較為方便。

第一層是“測(cè)試工具層”:

  • util 包:用于實(shí)現(xiàn)測(cè)試過(guò)程中調(diào)用的工具類(lèi)方法,例如讀取配置文件、頁(yè)面元素的操作方法、操作Excel文件等。
  • conf 包:配置文件及全局變量。
  • test_data 目錄:Excel 數(shù)據(jù)文件,包含測(cè)試數(shù)據(jù)輸入、測(cè)試結(jié)果輸出。
  • log 目錄:日志輸出文件。
  • screenshot_path 目錄:異常截圖保存目錄。

第二層是“服務(wù)層”,相當(dāng)于對(duì)測(cè)試對(duì)象的一個(gè)業(yè)務(wù)封裝。對(duì)于接口測(cè)試,是對(duì)遠(yuǎn)程方法的一個(gè)實(shí)現(xiàn);對(duì)于頁(yè)面測(cè)試,是對(duì)頁(yè)面元素或操作的一個(gè)封裝。

  • page 包:對(duì)象庫(kù)層及操作層,將所有頁(yè)面的元素對(duì)象定位及其操作分別封裝成一個(gè)類(lèi)。

第三層是“測(cè)試用例邏輯層”,該層主要是將服務(wù)層封裝好的各個(gè)業(yè)務(wù)對(duì)象,組織成測(cè)試邏輯,進(jìn)行校驗(yàn)。

  • action 包:組裝單個(gè)用例的流程。

  • business_process 包:基于業(yè)務(wù)層和測(cè)試數(shù)據(jù)文件,執(zhí)行測(cè)試用例集合。

  • test_data 目錄:Excel 數(shù)據(jù)文件,包含測(cè)試數(shù)據(jù)輸入、測(cè)試結(jié)果輸出?!?br> 第四層是“測(cè)試場(chǎng)景層”,將測(cè)試用例組織成測(cè)試場(chǎng)景,實(shí)現(xiàn)各種級(jí)別 cases 的管理、冒煙,回歸等測(cè)試場(chǎng)景。

  • main.py:本 PO 框架的運(yùn)行主入口。

UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)
UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)

框架特點(diǎn)

  1. 通過(guò)配置文件,實(shí)現(xiàn)頁(yè)面元素定位方式和測(cè)試代碼的分離。
  2. 使用 PO 模式,封裝了網(wǎng)頁(yè)中的頁(yè)面元素,方便測(cè)試代碼調(diào)用,也實(shí)現(xiàn)了一處維護(hù)全局生效的目標(biāo)。
  3. 在 excel 文件中定義多組測(cè)試數(shù)據(jù),每個(gè)登錄用戶(hù)都一一對(duì)應(yīng)一個(gè)存放聯(lián)系人數(shù)據(jù)的 sheet,測(cè)試框架可自動(dòng)調(diào)用測(cè)試數(shù)據(jù)完成數(shù)據(jù)驅(qū)動(dòng)測(cè)試。
  4. 實(shí)現(xiàn)了測(cè)試執(zhí)行過(guò)程中的日志記錄功能,可以通過(guò)日志文件分析測(cè)試腳本執(zhí)行的情況。
  5. 在 excel 數(shù)據(jù)文件中,通過(guò)設(shè)定“測(cè)試數(shù)據(jù)是否執(zhí)行”列的內(nèi)容為 y 或 n,自定義選擇測(cè)試數(shù)據(jù),測(cè)試執(zhí)行結(jié)束后會(huì)在"測(cè)試結(jié)果列"中顯示測(cè)試執(zhí)行的時(shí)間和結(jié)果,方便測(cè)試人員查看。

3. 工程代碼示例

page 包

對(duì)象庫(kù)層及操作層,將所有頁(yè)面的元素對(duì)象定位及其操作分別封裝成一個(gè)類(lèi)。

login_page.py

from conf.global_var import *
from util.ini_parser import IniParser
from util.find_element_util import *


# 登錄頁(yè)面元素定位及操作
class LoginPage:

    def __init__(self, driver):
        self.driver = driver
        # 初始化跳轉(zhuǎn)登錄頁(yè)面
        self.driver.get(LOGIN_URL)
        # 初始化指定ini配置文件及指定分組
        self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_loginPage")

    # 獲取frame元素對(duì)象
    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())

    # 獲取用戶(hù)名輸入框元素對(duì)象
    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)

    # 清空用戶(hù)名輸入框操作
    def clear_username(self):
        self.get_username_input_obj().clear()

    # 輸入用戶(hù)名操作
    def input_username(self, value):
        self.get_username_input_obj().send_keys(value)

    # 獲取密碼輸入框元素對(duì)象
    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)

    # 獲取登錄按鈕對(duì)象
    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 *


# 登錄后主頁(yè)元素定位及操作
class HomePage:

    def __init__(self, driver):
        self.driver = driver
        # 初始化指定ini配置文件及指定分組
        self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_homePage")

    # 獲取“通訊錄”按鈕對(duì)象
    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 *


# 通訊錄頁(yè)面元素定位及操作
class ContactPage:

    def __init__(self, driver):
        self.driver = driver
        # 初始化指定ini配置文件及指定分組
        self.cf = IniParser(ELEMENT_FILE_PATH, "126mail_contactPersonPage")

    # 獲取新建聯(lián)系人按鈕對(duì)象
    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)

    # 獲取確定按鈕對(duì)象
    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ù)層,將一個(gè)或多個(gè)操作組合起來(lái)完成一個(gè)業(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ù)層和測(cè)試文件,實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)的測(cè)試執(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


# 封裝測(cè)試數(shù)據(jù)文件中用例的執(zhí)行邏輯
# 測(cè)試數(shù)據(jù)文件中的每個(gè)登錄賬號(hào)
def batch_login(test_data_file, browser_name, account_sheet_name):
        excel = Excel(test_data_file)
        # 獲取登錄賬號(hào)sheet頁(yè)數(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("登錄成功【用戶(hù)名:{}, 密碼:{}, 斷言關(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("登錄失敗【用戶(hù)名:{}, 密碼:{}, 斷言關(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)
            # 寫(xiě)入登錄用例的測(cè)試結(jié)果
            excel.change_sheet("測(cè)試結(jié)果")
            excel.write_row_data(account_headline_data, "red")
            excel.write_row_data(account_row_data)
            excel.save()

            # 切換另一個(gè)賬號(hào)時(shí)需先關(guān)閉瀏覽器,否則會(huì)自動(dòng)登錄
            driver.quit()


if __name__ == "__main__":
    batch_login(TEST_DATA_FILE_PATH, "chrome", "126賬號(hào)")

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


# 封裝測(cè)試數(shù)據(jù)文件中用例的執(zhí)行邏輯
# 測(cè)試數(shù)據(jù)文件中每個(gè)登錄賬號(hào)下,添加所有聯(lián)系人數(shù)據(jù)
def batch_login_and_add_contact(test_data_file, browser_name, account_sheet_name):
        excel = Excel(test_data_file)
        # 獲取登錄賬號(hào)sheet頁(yè)數(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("登錄成功【用戶(hù)名:{}, 密碼:{}, 斷言關(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("登錄失敗【用戶(hù)名:{}, 密碼:{}, 斷言關(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)
            # 寫(xiě)入登錄用例的測(cè)試結(jié)果
            excel.change_sheet("測(cè)試結(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]
            # 在測(cè)試結(jié)果中,一個(gè)賬號(hào)下的聯(lián)系人數(shù)據(jù)標(biāo)題行僅寫(xiě)一次
            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ī)號(hào):{}, 是否星標(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ī)號(hào):{}, 是否星標(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)
                # 寫(xiě)入登錄用例的測(cè)試結(jié)果
                excel.change_sheet("測(cè)試結(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()

            # 切換另一個(gè)賬號(hào)時(shí)需先關(guān)閉瀏覽器,否則會(huì)自動(dòng)登錄
            driver.quit()


if __name__ == "__main__":
    batch_login_and_add_contact(TEST_DATA_FILE_PATH, "chrome", "126賬號(hào)")

util 包

用于實(shí)現(xiàn)測(cè)試過(guò)程中調(diào)用的工具類(lèi)方法,例如讀取配置文件、頁(yè)面元素的操作方法、操作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工具類(lèi)初始化失?。骸緖}】文件不存在!".format(test_data_file_path))
            return
        if not test_data_file_path.endswith(".xlsx") or not test_data_file_path.endswith(".xlsx"):
            print("Excel工具類(lèi)初始化失?。骸緖}】文件非excel文件類(lèi)型!".format(test_data_file_path))
            return
        # 打開(kāi)指定excel文件
        self.wb = load_workbook(test_data_file_path)
        # 初始化默認(rèn)sheet
        self.ws = self.wb.active
        # 保存文件時(shí)使用的文件路徑
        self.test_data_file_path = test_data_file_path
        # 初始化紅、綠色,供樣式使用
        self.color_dict = {"red": "FFFF3030", "green": "FF008B00"}

    # 查看所有sheet名稱(chēng)
    def get_sheets(self):
        return self.wb.sheetnames

    # 根據(jù)sheet名稱(chēng)切換sheet
    def change_sheet(self, sheet_name):
        if sheet_name not in self.get_sheets():
            print("sheet切換失?。骸緖}】指定sheet名稱(chēng)不存在!".format(sheet_name))
            return
        self.ws = self.wb.get_sheet_by_name(sheet_name)

    # 返回當(dāng)前sheet的最大行號(hào)
    def max_row_num(self):
        return self.ws.max_row

    # 返回當(dāng)前sheet的最大列號(hào)
    def max_col_num(self):
        return self.ws.max_column

    # 獲取指定行數(shù)據(jù)(設(shè)定索引從0開(kāi)始)
    def get_one_row_data(self, row_no):
        if row_no < 0 or row_no > self.max_row_num()-1:
            print("輸入的行號(hào)【{}】有誤:需在0至最大行數(shù)之間!".format(row_no))
            return
        # API的索引從1開(kāi)始
        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("輸入的列號(hào)【{}】有誤:需在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開(kāi)始
        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ù)類(lèi)型有誤:需為列號(hào)或元組類(lèi)型!【{}】".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è)置的單元格長(zhǎng)度應(yīng)與數(shù)據(jù)長(zhǎng)度一致,否則默認(rèn)與之前行的長(zhǎng)度一致
        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


# 顯式等待一個(gè)對(duì)象
def find_element(driver, locate_method, locate_exp):
    # 顯式等待對(duì)象(最多等10秒,每0.2秒判斷一次等待的條件)
    return WebDriverWait(driver, 10, 0.2).until(lambda x: x.find_element(locate_method, locate_exp))


# 顯式等待一組對(duì)象
def find_elements(driver, locate_method, locate_exp):
    # 顯式等待對(duì)象(最多等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:

    # 初始化打開(kāi)指定ini文件并指定編碼
    def __init__(self, file_path, section):
        self.cf = configparser.ConfigParser()
        self.cf.read(file_path, encoding="utf-8")
        self.section = section

    # 獲取所有分組名稱(chēng)
    def get_sections(self):
        return self.cf.sections()

    # 獲取指定分組的所有鍵
    def get_options(self):
        return self.cf.options(self.section)

    # 獲取指定分組的鍵值對(duì)
    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)


# 返回中文格式的時(shí)間:xx時(shí)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 "{}時(shí){}分{}秒".format(hour, minute, second)


# 返回英文格式的時(shí)間: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)


# 返回中文格式的日期時(shí)間
def get_chinese_datetime():
    return get_chinese_date() + " " + get_chinese_time()


# 返回英文格式的日期時(shí)間
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 *


# 日志配置文件:多個(gè)logger,每個(gè)logger指定不同的handler
# handler:設(shè)定了日志輸出行的格式
#          以及設(shè)定寫(xiě)日志到文件(是否回滾)?還是到屏幕
#          還定了打印日志的級(jí)別
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("這是一個(gè)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)前時(shí)間為文件名
    file_name = get_chinese_time()
    file_path = os.path.join(dir, file_name+".png")
    try:
        driver.get_screenshot_as_file(file_path)
        # 返回截圖文件的絕對(duì)路徑
        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ū)動(dòng)路徑
CHROME_DRIVER = "E:\\auto_test_driver\\chromedriver.exe"
IE_DRIVER = "E:\\auto_test_driver\\IEDriverServer.exe"
FIREFOX_DRIVER = "E:\\auto_test_driver\\geckodriver.exe"

# 測(cè)試使用的瀏覽器
BROWSER_NAME = "chrome"

# 登錄主頁(yè)
LOGIN_URL = "https://mail.126.com"

# 日志配置文件路徑
LOG_CONF_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "conf", "logger.conf")

# 測(cè)試用例文件路徑
TEST_DATA_FILE_PATH = os.path.join(PROJECT_ROOT_PATH, "test_data", "測(cè)試用例.xlsx")

# 截圖保存路徑
SCREENSHOT_PATH = os.path.join(PROJECT_ROOT_PATH, "screenshot_path")

# 單元測(cè)試報(bào)告輸出目錄
UNITTEST_REPORT_PATH = os.path.join(PROJECT_ROOT_PATH, "report")

# 登錄賬號(hào)sheet頁(yè)數(shù)據(jù)列號(hào)
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頁(yè)數(shù)據(jù)列號(hào)
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 目錄

測(cè)試用例.xlsx:包含測(cè)試數(shù)據(jù)輸入、測(cè)試結(jié)果輸出

UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)
UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)
UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)

log 目錄

日志輸出文件:126_mail_test.log

...
...
2021-02-23 16:59:15 log_util.py[line:19] INFO 登錄成功【用戶(hù)名:zhangjun252950418, 密碼:zhangjun123, 斷言關(guān)鍵字:退出】
2021-02-23 16:59:20 log_util.py[line:19] INFO 添加聯(lián)系人成功【姓名:lily, 郵箱:lily@qq.com, 手機(jī)號(hào):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ī)號(hào):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ī)號(hào):157xxxxxx9, 是否星標(biāo)聯(lián)系人:否, 備注:, 斷言關(guān)鍵字:李四】
...
...

screenshot_path 目錄

異常截圖保存目錄:

UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)

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 *


# 示例組裝:冒煙測(cè)試
def smoke_test():
    batch_login(TEST_DATA_FILE_PATH, "chrome", "126賬號(hào)")


# 示例組裝:全量測(cè)試
def full_test():
    batch_login_and_add_contact(TEST_DATA_FILE_PATH, "chrome", "126賬號(hào)")


if __name__ == "__main__":
    # smoke_test()
    full_test()

最后:下方這份完整的自動(dòng)化測(cè)試視頻學(xué)習(xí)教程已經(jīng)整理上傳完成,朋友們?nèi)绻枰梢宰孕忻赓M(fèi)領(lǐng)取 【保證100%免費(fèi)】

UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)

UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-457734.html

到了這里,關(guān)于UI 自動(dòng)化測(cè)試框架:PO 模式+數(shù)據(jù)驅(qū)動(dòng)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 如何搭建自動(dòng)化測(cè)試框架?資深測(cè)試整理的PO模式,一套打通自動(dòng)化...

    如何搭建自動(dòng)化測(cè)試框架?資深測(cè)試整理的PO模式,一套打通自動(dòng)化...

    Po模型介紹 1、簡(jiǎn)介 在自動(dòng)化中,Selenium自動(dòng)化測(cè)試中有一個(gè)名字經(jīng)常被提及PageObject(思想與面向?qū)ο蟮奶卣飨嗤?,通常PO模型可以大大提高測(cè)試用例的維護(hù)效率 2、為什么要用PO 基于selenium2開(kāi)始ui自動(dòng)化測(cè)試腳本的編寫(xiě)不是多么艱巨的任務(wù)。只需要定位到元素,執(zhí)行對(duì)應(yīng)元素的

    2024年02月13日
    瀏覽(98)
  • UI自動(dòng)化測(cè)試:Selenium+PO模式+Pytest+Allure整合

    UI自動(dòng)化測(cè)試:Selenium+PO模式+Pytest+Allure整合

    本人目前工作中未涉及到WebUI自動(dòng)化測(cè)試,但為了提升自己的技術(shù),多學(xué)習(xí)一點(diǎn)還是沒(méi)有壞處的,廢話不多說(shuō)了,目前主流的webUI測(cè)試框架應(yīng)該還是selenium,考慮到可維護(hù)性、拓展性、復(fù)用性等,我們采用PO模式去寫(xiě)我們的腳本,本文檔也主要整合了Selenium+PO模式+Pytest+Allure,下

    2024年02月08日
    瀏覽(506)
  • 微信小程序自動(dòng)化測(cè)試框架 Minium——PO模式測(cè)試用例

    微信小程序自動(dòng)化測(cè)試框架 Minium——PO模式測(cè)試用例

    本文主要介紹PO模式的測(cè)試用例,PO模式優(yōu)點(diǎn)及層級(jí)間的關(guān)系,相關(guān)配置及運(yùn)行 minitest的測(cè)試小程序和測(cè)試case:minitest-demo miniprogram-demo :測(cè)試小程序 testcase :測(cè)試case,同時(shí)也包含文檔的測(cè)試case testcase-PO :Page Object(PO) 模式的測(cè)試case PO模式是自動(dòng)化測(cè)試項(xiàng)目開(kāi)發(fā)實(shí)踐的最佳設(shè)

    2024年02月07日
    瀏覽(110)
  • 讓自動(dòng)化測(cè)試秒殺繁瑣操作?試試PO模式設(shè)計(jì)框架

    讓自動(dòng)化測(cè)試秒殺繁瑣操作?試試PO模式設(shè)計(jì)框架

    目錄:導(dǎo)讀 引言 po模式 優(yōu)勢(shì): ?目錄解釋?zhuān)?頁(yè)面對(duì)象設(shè)計(jì)模式: base基礎(chǔ)層: page對(duì)象層: ?test:測(cè)試層 data數(shù)據(jù)層: ?common層: ?untils: ?config層: run層: report: 結(jié)語(yǔ) 你是否曾經(jīng)因?yàn)槊看胃鹿δ芏家匦聦?xiě)一堆自動(dòng)化測(cè)試代碼而感到疲憊不堪? 或者因?yàn)轫?yè)面元素的頻繁變

    2024年02月02日
    瀏覽(101)
  • 多測(cè)師肖sir___ui自動(dòng)化測(cè)試po框架(升級(jí))

    多測(cè)師肖sir___ui自動(dòng)化測(cè)試po框架(升級(jí))

    ui自動(dòng)化測(cè)試po框架(升級(jí)) po框架 一、ui自動(dòng)化po框架介紹 (1)PO是Page Object的縮寫(xiě)(pom模型) (2)業(yè)務(wù)流程與頁(yè)面元素操作分離的模式,可以簡(jiǎn)單理解為每個(gè)頁(yè)面下面都有一個(gè)配置class, 配置class就用來(lái)維護(hù)頁(yè)面元素或操作方法 (3)提高測(cè)試用例的可維護(hù)性、可讀取性

    2024年01月19日
    瀏覽(99)
  • 多測(cè)師肖sir___ui自動(dòng)化測(cè)試po框架講解版

    多測(cè)師肖sir___ui自動(dòng)化測(cè)試po框架講解版

    po框架 一、ui自動(dòng)化po框架介紹 (1)PO是Page Object的縮寫(xiě)(pom模型) (2)業(yè)務(wù)流程與頁(yè)面元素操作分離的模式,可以簡(jiǎn)單理解為每個(gè)頁(yè)面下面都有一個(gè)配置class, 配置class就用來(lái)維護(hù)頁(yè)面元素或操作方法 (3)提高測(cè)試用例的可維護(hù)性、可讀取性 對(duì)比:傳統(tǒng)的設(shè)計(jì)測(cè)試用例存

    2024年02月02日
    瀏覽(92)
  • Selenium Web自動(dòng)化測(cè)試——基于unittest框架的PO設(shè)計(jì)模式

    Selenium Web自動(dòng)化測(cè)試——基于unittest框架的PO設(shè)計(jì)模式

    ??? 交流討論: 歡迎加入我們一起學(xué)習(xí)! ?? 資源分享 : 耗時(shí)200+小時(shí)精選的「軟件測(cè)試」資料包 ??? 教程推薦: 火遍全網(wǎng)的《軟件測(cè)試》教程?? ?? 歡迎點(diǎn)贊 ?? 收藏 ?留言 ?? 如有錯(cuò)誤敬請(qǐng)指正! 前面一直在講接口自動(dòng)化測(cè)試框架與案例分享,很少講Selenium這個(gè)We

    2024年03月21日
    瀏覽(93)
  • 自動(dòng)化測(cè)試po模式是什么

    自動(dòng)化測(cè)試po模式是什么

    全稱(chēng):page object model ?簡(jiǎn)稱(chēng):POM/PO PO模式最核心的思想是分層,實(shí)現(xiàn)松耦合!實(shí)現(xiàn)腳本重復(fù)使用,實(shí)現(xiàn)腳本易維護(hù)性! 主要分三層: 1.基礎(chǔ)層BasePage:封裝一些最基礎(chǔ)的selenium的原生的api方法,元素定位,框架跳轉(zhuǎn)等。 2.PO層:元素定位、獲得元素對(duì)象,頁(yè)面動(dòng)作 3.測(cè)試用例層

    2024年01月19日
    瀏覽(93)
  • Selenium自動(dòng)化測(cè)試設(shè)計(jì)模式-PO模式

    Selenium自動(dòng)化測(cè)試設(shè)計(jì)模式-PO模式

    在python自動(dòng)化過(guò)程中,Selenium自動(dòng)化測(cè)試中有一個(gè)名字常常被提及PageObject(思想與面向?qū)ο蟮奶匦韵嗤?,通過(guò)PO模式可以大大提高測(cè)試用例的維護(hù)效率。 不了解po設(shè)計(jì)模式的可自行百度 面向?qū)ο蟮奶匦?:封裝、繼承、多態(tài) 傳統(tǒng)測(cè)試腳本的弊端: 測(cè)試腳本分離,維護(hù)成本高

    2023年04月08日
    瀏覽(88)
  • 【W(wǎng)EB自動(dòng)化測(cè)試】-PO設(shè)計(jì)模式

    如用例多了,那么登陸的代碼就需要在每個(gè)模塊化中都加入,所以考慮公共部分提取出來(lái),做成一個(gè)公共的工具類(lèi)。習(xí)慣命名為utils.py。 定義獲取驅(qū)動(dòng)對(duì)象的工具類(lèi) 封裝\\\"獲取彈出框的提示信息\\\" PO是Page Object的縮寫(xiě),PO模式是自動(dòng)化測(cè)試項(xiàng)目開(kāi)發(fā)實(shí)踐的最佳設(shè)計(jì)模式之一。核心

    2024年02月16日
    瀏覽(98)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包