之前寫的代碼中都沒有加入異常處理,規范寫法,應該在每次查找元素或操作時加上異常處理、日志信息、失敗截圖等,如下:
def input_account(self, account): '''輸入賬號''' try: WebDriverWait(self.driver, 20).until(EC.visibility_of_element_located(self.account)) self.driver.find_element(*self.account).send_keys(account) except Exception: raise
但是為所有的查找元素和操作都加上這些耗時且不方便維護,為了簡化操作,可以把一些公用的方法封裝到 BasePage 類,其它頁面 page 直接繼承 BasePage 即可調用公共方法。
BasePage 類:
1. 封裝基本函數:執行日志、失敗截圖、異常處理等
2. 所有頁面公共操作方法
文件結構:
示例代碼:
base_page.py
import os import time from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from common.logger import logger import configs class BasePage: def __init__(self, driver): self.driver = driver def open(self, url): self.driver.get(url) def wait_ele_visible(self, locator:tuple, doc="", timeout=30, poll_frequency=0.5, ignored_exceptions=None): ''' 等待元素可見 :param locator: 元素定位方式,元組形式 :param timeout: 超時時間,默認30秒 :param poll_frequency: 輪詢時間,默認0.5秒 :param ignored_exceptions: 要忽略的異常 :param doc: 截圖模塊名稱,若沒有則只以時間戳明明 :return: ''' logger.info(f"等待元素 {locator} 可見") try: WebDriverWait(self.driver, timeout, poll_frequency, ignored_exceptions).until(EC.visibility_of_element_located(locator)) except Exception as e: logger.exception(f"等待元素 {locator} 可見失敗") self.save_screenshot(doc) raise e # 查找元素 def get_element(self, locator: tuple, doc="")->WebElement: logger.info(f"查找元素 {locator} ") try: return self.driver.find_element(*locator) except: logger.exception(f"查找元素 {locator} 失敗") self.save_screenshot(doc) raise # 點擊元素 def click_element(self, locator:tuple, doc=""): ele = self.get_element(locator, doc) try: ele.click() except: logger.exception(f"點擊元素 {locator} 失敗") self.save_screenshot(doc) raise # 輸入文本 def input_text(self, locator:tuple, text, doc=""): ele = self.get_element(locator, doc) try: ele.send_keys(text) except: logger.exception(f"元素 {locator} 輸入文本 {text} 失敗") self.save_screenshot(doc) raise # iframe 切換 def switch_to_iframe(self, locator:tuple, doc=""): ele = self.get_element(locator, doc) try: self.driver.switch_to.frame(ele) except: logger.exception(f"切換到iframe {locator} 失敗") self.save_screenshot(doc) raise # 失敗截圖 def save_screenshot(self, filename): if not os.path.exists(configs.SCREENSHOT_PATH): os.mkdir(configs.SCREENSHOT_PATH) # 圖片名稱:模塊名-頁面名-函數名-年月日時分秒.png file_name = configs.SCREENSHOT_PATH + filename + "_" + time.strftime("%Y%m%d%H%M%S") + ".png" self.driver.save_screenshot(file_name) logger.info(f"成功獲取截圖,路徑:{file_name}")
demo_login_page.py,頁面 page 類需繼承 BasePage 類
import os from configparser import ConfigParser from common.base_page import BasePage class LoginPage(BasePage): def get_locator(self): conf = ConfigParser() print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\page_locator\\demo_locator.ini") conf.read(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\page_locator\\demo_locator.ini", encoding="utf-8") self.url = conf.get("Base", "url") section = "LoginPageLocator" # eval 將字符串元組轉換為元組 self.login_by_pwd_btn = eval(conf.get(section, "login_by_pwd_btn")) self.account = eval(conf.get(section, "account")) self.pwd = eval(conf.get(section, "pwd")) self.login_btn = eval(conf.get(section, "login_btn")) def __init__(self, driver): super().__init__(driver) self.get_locator() def login(self, account, password): doc = "登錄頁面_登錄" self.driver.get(self.url) # 打開登錄頁面 self.click_element(self.login_by_pwd_btn, doc) # 點擊密碼登錄 self.input_text(self.account, account, doc) # 輸入賬號 self.input_text(self.pwd, password, doc) # 輸入密碼 self.click_element(self.login_btn, doc) # 點擊登錄
demo_home_page.py,頁面 page 類需繼承 BasePage 類
import os from configparser import ConfigParser from common.base_page import BasePage class HomePage(BasePage): def get_locator(self): conf = ConfigParser() conf.read(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\page_locator\\demo_locator.ini",encoding="utf-8") section = "HomePageLocator" self.account_info = eval(conf.get(section, "account_info")) def __init__(self, driver): super().__init__(driver) self.get_locator() def is_account_info_exist(self): try: self.wait_ele_visible(self.account_info) return True except Exception: return False
test_demo.py
from pytest_assume.plugin import assume from selenium import webdriver from po.page_object.demo_home_page import HomePage from po.page_object.demo_login_page import LoginPage class TestLogin(): def setup_class(self): self.driver = webdriver.Chrome() self.driver.maximize_window() self.driver.implicitly_wait(10) def teardown_class(self): self.driver.quit() def test_demo(self): self.login_page = LoginPage(self.driver) self.home_page = HomePage(self.driver) self.login_page.login("15348369418", "Pan910124") with assume: assert self.home_page.is_account_info_exist()
對於一些通用的方法,我們也可根據自己的需要進行封裝,譬如從 demo_locator.ini 文件獲取數據,我們可以封裝一個工具類 handle_ini.py 放在 common 模塊下:
import os from configparser import ConfigParser class HandleIni(ConfigParser): # 已字典格式返回 ini 文件中某個 section 的數據 def get_locator_dict(self, locator_file, section): conf = ConfigParser() conf.read(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "\\po\\page_locator\\" + locator_file, encoding="utf-8") section_data = {} for key in conf.options(section): section_data[key] = conf.get(section, key) return section_data
那么其他頁面類就可以使用 HandleIni 工具類來獲取數據:
# demo_home_page.py from common.base_page import BasePage from common.handle_ini import HandleIni class HomePage(BasePage): def get_locator(self): locators = HandleIni().get_locator_dict("demo_locator.ini", "HomePageLocator") self.account_info = eval(locators.get("account_info")) def __init__(self, driver): super().__init__(driver) self.get_locator() def is_account_info_exist(self): try: self.wait_ele_visible(self.account_info) return True except Exception: return False