web自動化針對PO模式進行二次封裝之basepage


  在PO模式當中,我們做到了頁面對象與測試用例的分離,但在頁面對象編寫時,我們仍然還有優化的空間。頁面對象有一些共同的基本操作,可以封裝起來,並可以在基本操作當中加上日志和異常截圖的處理。比如說我們在查找元素時,都需要等待,在PO模式當中,需要都寫上等待和查找元素,那么就可以將其封裝起來,包括其它的一些比如:文本獲取、元素屬性獲取、鼠標操作、窗口切換、iframe切換、alert彈框關閉、文件上傳、下拉框選擇......

  當腳本運行的過程中,出現了用例失敗,我們希望可以通過日志和截圖來分析失敗的原因,selenium並沒有主動生成日志和截圖,所以需要在代碼中加上,使用webdriver的save_screenshot方法來截圖,日志和截圖會記錄自動化用例的執行過程,並且,在用例的任何一個步驟出現了異常都會記錄異常信息並生成相應的截圖。當然為了更美觀地跟allure集成,自然也需要將以異常截圖展示在allure報告中,以下就是basepage.py的部分內容:

import time
from datetime import datetime
import allure
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.remote.webdriver import WebDriver
from Common.handle_logger import case_logger
from Common.constants import OUTPUTS_DIR
from Common.upload_file import upload


class BasePage:
    '''
    BasePage類,針對PageObjects類的二次封裝
    '''

    def __init__(self, driver: WebDriver):
        self.driver = driver

    def wait_element_to_be_visible(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        等待元素可見
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("開始等待頁面元素<{}>是否可見!".format(loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.visibility_of_element_located(loc))
        except Exception as e:
            case_logger.error("頁面元素<{}>等待可見失敗!".format(loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            end_time = time.time()
            case_logger.info("頁面元素<{}>等待可見,等待時間:{}秒".format(loc, round(end_time - start_time, 2)))

    def wait_element_to_be_click(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        等待元素可點擊
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("開始等待頁面元素<{}>是否可點擊!".format(loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.element_to_be_clickable(loc))
        except Exception as e:
            case_logger.error("頁面元素<{}>等待可點擊失敗!".format(loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            end_time = time.time()
            case_logger.info("頁面元素<{}>等待可點擊,等待時間:{}秒".format(loc, round(end_time - start_time, 2)))

    def wait_element_to_be_exist(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        等待元素存在
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("開始等待頁面元素<{}>是否存在!".format(loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.presence_of_element_located(loc))
        except Exception as e:
            case_logger.error("頁面元素<{}>等待存在失敗!".format(loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            end_time = time.time()
            case_logger.info("頁面元素<{}>等待存在,等待時間:{}秒".format(loc, round(end_time - start_time, 2)))

    def save_screenshot(self, img_doc):
        '''
        頁面截屏保存截圖
        :param img_doc: 截圖說明
        :return:
        '''
        file_name = OUTPUTS_DIR + "\\{}_{}.png".format(datetime.strftime(datetime.now(), "%Y%m%d%H%M%S"), img_doc)
        self.driver.save_screenshot(file_name)
        with open(file_name, mode='rb') as f:
            file = f.read()
        allure.attach(file, img_doc, allure.attachment_type.PNG)
        case_logger.info("頁面截圖文件保存在:{}".format(file_name))

    def get_element(self, loc, img_doc):
        '''
        獲取頁面中的元素
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :return: WebElement對象
        '''
        case_logger.info("在{}中查找元素<{}>".format(img_doc, loc))
        try:
            ele = self.driver.find_element(*loc)
        except Exception as e:
            case_logger.error("在{}中查找元素<{}>失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            return ele

    def get_elements(self, loc, img_doc):
        '''
        獲取頁面中的所有元素
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :return: WebElement對象
        '''
        case_logger.info("在{}中查找所有元素<{}>".format(img_doc, loc))
        try:
            ele = self.driver.find_elements(*loc)
        except Exception as e:
            case_logger.error("在{}中查找所有元素<{}>失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            return ele

    def input_text(self, text, loc, img_doc, timeout=20, frequency=0.5):
        '''
        對輸入框輸入文本內容
        :param text: 輸入的文本內容
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}中輸入元素<{}>的內容為{}".format(img_doc, loc, text))
            self.wait_element_to_be_visible(loc, img_doc, timeout, frequency)
            self.get_element(loc, img_doc).send_keys(text)
        except Exception as e:
            case_logger.error("在元素<{}>中輸入內容{}失敗!".format(loc, text))
            self.save_screenshot(img_doc)
            raise e

    def clear_text(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        清除文本框的內容
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}中清除元素<{}>的文本內容".format(img_doc, loc))
            self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
            self.get_element(loc, img_doc).clear()
        except Exception as e:
            case_logger.error("在{}中清除元素<{}>的文本內容失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e

    def click_button(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        點擊按鈕
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}中點擊元素<{}>".format(img_doc, loc))
            self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
            self.get_element(loc, img_doc).click()
        except Exception as e:
            case_logger.error("在{}中點擊元素<{}>失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e

    def get_element_text(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        獲取WebElement對象的文本值
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return: WebElement對象的文本值
        '''
        try:
            case_logger.info("在{}中獲取元素<{}>的文本值".format(img_doc, loc))
            self.wait_element_to_be_visible(loc, img_doc, timeout, frequency)
            text = self.get_element(loc, img_doc).text
        except Exception as e:
            case_logger.error("在{}中獲取元素<{}>的文本值失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            case_logger.info("獲取到的元素文本值為:{}".format(text))
            return text

    def get_elements_text(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        獲取WebElement對象的所有文本值
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return: WebElement對象的文本值的列表
        '''
        try:
            case_logger.info("在{}中獲取元素<{}>的所有文本值".format(img_doc, loc))
            self.wait_element_to_be_visible(loc, img_doc, timeout, frequency)
            all_text = self.get_elements(loc, img_doc)
            text_list = []
            for one_text in all_text:
                text_list.append(one_text.text)
        except Exception as e:
            case_logger.error("在{}中獲取元素<{}>的所有文本值失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            case_logger.info("獲取到的元素文本值列表為:{}".format(text_list))
            return text_list

    def get_element_attr(self, attr_name, loc, img_doc, timeout=20, frequency=0.5):
        '''
        獲取WebElement對象的屬性值
        :param attr_name: 屬性名稱
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return: WebElement對象的屬性值
        '''
        try:
            case_logger.info("在{}中獲取元素<{}>的屬性{}的值".format(img_doc, loc, attr_name))
            self.wait_element_to_be_exist(loc, img_doc, timeout, frequency)
            value = self.get_element(loc, img_doc).get_attribute(attr_name)
        except Exception as e:
            case_logger.error("在{}中獲取元素<{}>的屬性{}的值失敗!".format(img_doc, loc, attr_name))
            self.save_screenshot(img_doc)
            raise e
        else:
            case_logger.info("獲取到的元素屬性{}的值為{}".format(attr_name, value))
            return value

    def switch_to_frame(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        切換iframe頁面
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}中根據元素<{}>進行iframe切換".format(img_doc, loc))
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.frame_to_be_available_and_switch_to_it(loc))
        except Exception as e:
            case_logger.error("在{}中根據元素<{}>進行iframe切換失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            end_time = time.time()
            case_logger.info("在{}中根據元素<{}>進行iframe切換,等待時間:{}秒".
                             format(img_doc, loc, round(end_time - start_time, 2)))

    def switch_to_default_content(self, img_doc):
        '''
        切換iframe到main頁面
        :param img_doc: 截圖說明
        :return:
        '''
        try:
            case_logger.info("切換iframe到main頁面")
            self.driver.switch_to.default_content()
        except Exception as e:
            case_logger.error("切換iframe到main頁面失敗!")
            self.save_screenshot(img_doc)
            raise e

    def switch_to_parent(self, img_doc):
        '''
        切換iframe到上一層頁面
        :param img_doc: 截圖說明
        :return:
        '''
        try:
            case_logger.info("切換iframe到上一層頁面")
            self.driver.switch_to.parent_frame()
        except Exception as e:
            case_logger.error("切換iframe到上一層頁面失敗!")
            self.save_screenshot(img_doc)
            raise e

    def upload_file(self, filename, img_doc, browser_type="chrome"):
        '''
        非input標簽的文件上傳
        :param filename: 文件名(絕對路徑)
        :param img_doc: 截圖說明
        :param browser_type: 瀏覽器類型
        :return:
        '''
        try:
            case_logger.info("上傳文件({})".format(filename))
            time.sleep(2)
            upload(filePath=filename, browser_type=browser_type)
        except Exception as e:
            case_logger.error("上傳文件({})失敗!".format(filename))
            self.save_screenshot(img_doc)
            raise e
        else:
            time.sleep(2)

    def suspend_mouse(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        鼠標懸浮
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}上根據元素<{}>進行懸浮".format(img_doc, loc))
            self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
            ele = self.get_element(loc, img_doc)
            ActionChains(self.driver).move_to_element(ele).perform()
        except Exception as e:
            case_logger.error("在{}上根據元素<{}>進行懸浮失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e

    def alert_close(self, img_doc, alert_type='alert', text=None, timeout=20, frequency=0.5):
        '''
        彈框關閉
        :param img_doc: 截圖說明
        :param alert_type: 彈框類型:alert/confirm/prompt
        :param text: prompt彈框輸入的文本
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}中切換並關閉{}類型的彈框".format(img_doc, alert_type))
            start_time = time.time()
            WebDriverWait(self.driver, timeout, frequency).until(EC.alert_is_present())
            if alert_type in ['alert', 'confirm']:
                self.driver.switch_to.alert.accept()
            elif alert_type == 'prompt':
                self.driver.switch_to.alert.send_keys(text)
                self.driver.switch_to.alert.accept()
            else:
                case_logger.error("不支持{},請確認alert的類型".format(alert_type))
        except Exception as e:
            case_logger.error("在{}中切換並關閉{}類型的彈框失敗!".format(img_doc, alert_type))
            self.save_screenshot(img_doc)
            raise e
        else:
            end_time = time.time()
            case_logger.info("在{}中切換並關閉{}類型的彈框,等待時間:{}秒".
                             format(img_doc, alert_type, round(end_time - start_time, 2)))

    def select_action(self, loc, img_doc, content, select_type, timeout=20, frequency=0.5):
        '''
        Select操作
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param content: select_by_方法的入參
        :param select_type: select類型
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}上根據元素<{}>以{}方式進行下拉選擇".format(img_doc, loc, select_type))
            self.wait_element_to_be_click(loc, img_doc, timeout, frequency)
            ele = self.get_element(loc, img_doc)
            if select_type == 'index':
                Select(ele).select_by_index(content)
            elif select_type == 'value':
                Select(ele).select_by_value(content)
            elif select_type == 'text':
                Select(ele).select_by_visible_text(content)
            else:
                case_logger.error("不支持{},請確認Select的類型".format(select_type))
        except Exception as e:
            case_logger.error("在{}上根據元素<{}>以{}方式進行下拉選擇失敗!".format(img_doc, loc, select_type))
            self.save_screenshot(img_doc)
            raise e

    def switch_to_windows(self, loc, img_doc, timeout=20, frequency=0.5):
        '''
        窗口切換
        :param loc: 元素定位的XPATH元組表達式
        :param img_doc: 截圖說明
        :param timeout: 等待的超時時間
        :param frequency: 輪詢頻率
        :return:
        '''
        try:
            case_logger.info("在{}中根據元素<{}>進行窗口切換".format(img_doc, loc))
            cur_handles = self.driver.window_handles  # 獲取點擊之前的窗口總數
            start_time = time.time()
            self.click_button(loc, img_doc, timeout, frequency)  # 點擊按鈕后新的窗口出現
            WebDriverWait(self.driver, timeout, frequency).until(EC.new_window_is_opened(cur_handles))
            wins = self.driver.window_handles  # 再次獲取窗口總數
            self.driver.switch_to.window(wins[-1])  # 切換到新的頁面
        except Exception as e:
            case_logger.error("在{}中根據元素<{}>進行窗口切換失敗!".format(img_doc, loc))
            self.save_screenshot(img_doc)
            raise e
        else:
            end_time = time.time()
            case_logger.info("在{}中根據元素<{}>進行窗口切換,等待時間:{}秒".
                             format(img_doc, loc, round(end_time - start_time, 2)))

  basepage模塊在web自動化里面起到了至關重要的作用,它起到了優化代碼,多次調用,更高的穩定性,日志記錄,異常截圖等優點,在PageObjects里面直接繼承它用來封裝頁面的各種行為!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM