selenium(6)----PO设计模式

一、Page Object 设计模式

UI 自动化测试最大的障碍或者成本最大的地方就在于页面的频繁变化。UI 自动化测试过于依赖于界面,界面变化意味着你的代码无法使用,需要更新维护。

虽然我们可以通过选择更有效的用例来达到降低维护成本的目的,但是毕竟以涉及到 UI 元素变化,我们的代码就需要改变。

目前 UI 自动化测试中最流行和达成共识的做法是是采用 Page Object (简称 PO) 设计模式,使用这种模式可以有效降低 UI 自动化测试代码的维护量。

二、基本思想

在 UI 自动化测试中对页面元素的处理引入面向对象思想。从测试用例中剥离页面元素操作,并以页面为单位的类将页面元素上的定位方式及操作进行封装。

什么意思呢?不采用 PO 模式的时候,我们相当于在测试用例中直接调用 Selenium 中的各种 API,由这些 API 去操作页面元素。而 PO 模式相当于在测试用例和 Selenium 之间增加了一层 PO 层。通过自定义的页面元素,再去调用 Selenium 中的 API,同时也将测试用例与 Selenium 的元素操作做了分离。

常规模式:tests---->selenium---->html
PO模式:tests---->page object---->selenium---->html

也就是说,你的测试用例 TestCase 中不会再出现诸如 find_element() 之类的方法了。

通过对页面元素的封装,将页面涉及到的元素定位和操作全部集中到了一起,不会再零散的出现在各个测试用例中。

当页面发生变化时,你不需要到各个测试用例中去查找需要修改的代码,只需要在对应的页面类中去修改其定位方式和操作即可。

三、Page Object 设计原理

Page Object设计模式是Selenium自动化测试项目的最佳设计模式之一,强调测试、逻辑、数据和驱动相互分离。

主要是将每一个页面设计为一个Class,其中包含页面中需要测试的元素(按钮,输入框,标题等),这样在Selenium测试页面中可以通过调用页面类来获取页面元素,这样巧妙的避免了当页面元素id或者位置变化时,需要改测试页面代码的情况。当页面元素id变化时,只需要更改测试页Class中页面的属性即可。

它的好处如下:

  • 集中管理元素对象
  • 集中管理一个page内的公共方法
  • 后期维护方便

四、Page Object 的对象

1.WebDriver封装

  • 这里是对Selenium的封装,完成封装以后的基本封装代码。

2.Page 基类

  • 设计了一个基本的Page类,以便所有的页面进行继承,该类标明了一个sub page类的基本功能和公共的功能。

3.Sub Pages(s)子类

  • 具体的页面的类,定义了某个具体的页面的功能。

4.Tests 类

  • 这部分描述的是具体的测试用例。

5.定义Test Suite

  • 多个测试用例添加在一个Test套件里面,一起执行。

6.定义Test Runner

  • 设计测试的Runner,开启整个测试,并且对测试的结果生成HTML测试报告,并通过邮件发送到指定邮箱。

7.定义测试的主入口

  • 定义测试的主要入口类,代码的入口

下面是栗子:
1.WebDriver封装

# coding=utf-8
from selenium import webdriver
from selenium.webdriver.support.select import Select


class AutomateDriver(object):
    """
    a simple demo of selenium framework tool
    """

    def __init__(self):

        driver = webdriver.Firefox()
        try:
            self.driver = driver
        except Exception:
            raise NameError("Firefox Not Found!")


    def clearCookies(self):
        """
        clear all cookies after driver init
        """
        self.driver.delete_all_cookies()

    def refreshBrowser(self):
        self.driver.refresh()

    def maximizeWindow(self):
        self.driver.maximize_window()

    def navigate(self, url):
        self.driver.get(url)

    def quitBrowser(self):
        self.driver.quit()

    def closeBrowser(self):
        self.driver.close()

    def getElement(self, selector):
        """
        to locate element by selector
        :arg
        selector should be passed by an example with "i,xxx"
        "x,//*[@id='langs']/button"
        :returns
        DOM element
        """
        if ',' not in selector:
            return self.driver.find_element_by_id(selector)
        selector_by = selector.split(',')[0]
        selector_value = selector.split(',')[1]

        if selector_by == "i" or selector_by == 'id':
            element = self.driver.find_element_by_id(selector_value)
        elif selector_by == "n" or selector_by == 'name':
            element = self.driver.find_element_by_name(selector_value)
        elif selector_by == "c" or selector_by == 'class_name':
            element = self.driver.find_element_by_class_name(selector_value)
        elif selector_by == "l" or selector_by == 'link_text':
            element = self.driver.find_element_by_link_text(selector_value)
        elif selector_by == "p" or selector_by == 'partial_link_text':
            element = self.driver.find_element_by_partial_link_text(selector_value)
        elif selector_by == "t" or selector_by == 'tag_name':
            element = self.driver.find_element_by_tag_name(selector_value)
        elif selector_by == "x" or selector_by == 'xpath':
            element = self.driver.find_element_by_xpath(selector_value)
        elif selector_by == "s" or selector_by == 'selector_selector':
            element = self.driver.find_element_by_css_selector(selector_value)
        else:
            raise NameError("Please enter a valid type of targeting elements.")

        return element

    def type(self, selector, text):
        """
        Operation input box.

        Usage:
        driver.type("i,el","selenium")
        """
        el = self.getElement(selector)
        el.clear()
        el.send_keys(text)

    def click(self, selector):
        """
        It can click any text / image can be clicked
        Connection, check box, radio buttons, and even drop-down box etc..

        Usage:
        driver.click("i,el")
        """
        el = self.getElement(selector)
        el.click()

    def selectByIndex(self, selector, index):
        """
        It can click any text / image can be clicked
        Connection, check box, radio buttons, and even drop-down box etc..

        Usage:
        driver.select_by_index("i,el")
        """
        el = self.getElement(selector)
        Select(el).select_by_index(index)

    def clickByText(self, text):
        """
        Click the element by the link text

        Usage:
        driver.click_text("新闻")
        """
        self.getElement('p,' + text).click()

    def submit(self, selector):
        """
        Submit the specified form.

        Usage:
        driver.submit("i,el")
        """
        el = self.getElement(selector)
        el.submit()

    def executeJs(self, script):
        """
        Execute JavaScript scripts.

        Usage:
        driver.js("window.scrollTo(200,1000);")
        """
        self.driver.execute_script(script)

    def getAttribute(self, selector, attribute):
        """
        Gets the value of an element attribute.

        Usage:
        driver.get_attribute("i,el","type")
        """
        el = self.getElement(selector)
        return el.getAttribute(attribute)

    def getText(self, selector):
        """
        Get element text information.

        Usage:
        driver.get_text("i,el")
        """
        el = self.getElement(selector)
        return el.text

    def getDisplay(self, selector):
        """
        Gets the element to display,The return result is true or false.

        Usage:
        driver.get_display("i,el")
        """
        el = self.getElement(selector)
        return el.is_displayed()

    def getTitle(self):
        '''
        Get window title.

        Usage:
        driver.get_title()
        '''
        return self.driver.title

    def getUrl(self):
        """
        Get the URL address of the current page.

        Usage:
        driver.get_url()
        """
        return self.driver.current_url

    def acceptAlert(self):
        '''
            Accept warning box.

            Usage:
            driver.accept_alert()
            '''
        self.driver.switch_to.alert.accept()

    def dismissAlert(self):
        '''
        Dismisses the alert available.

        Usage:
        driver.dismissAlert()
        '''
        self.driver.switch_to.alert.dismiss()

    def implicitlyWait(self, secs):
        """
        Implicitly wait. All elements on the page.

        Usage:
        driver.implicitly_wait(10)
        """
        self.driver.implicitly_wait(secs)

    def switchFrame(self, selector):
        """
        Switch to the specified frame.

        Usage:
        driver.switch_to_frame("i,el")
        """
        el = self.getElement(selector)
        self.driver.switch_to.frame(el)

    def switchDefaultFrame(self):
        """
        Returns the current form machine form at the next higher level.
        Corresponding relationship with switch_to_frame () method.

        Usage:
        driver.switch_to_frame_out()
        """
        self.driver.switch_to.default_content()

    def openNewWindow(self, selector):
        '''
        Open the new window and switch the handle to the newly opened window.

        Usage:
        driver.open_new_window()
        '''
        original_windows = self.driver.current_window_handle
        el = self.getElement(selector)
        el.click()
        all_handles = self.driver.window_handles
        for handle in all_handles:
            if handle != original_windows:
                self.driver._switch_to.window(handle)

2.Base Page类

class RanzhiBasePage():
    def __init__(self, driver, baseUrl):
        """
        构造方法
        :param driver: 封装好的webdriver
        :param baseUrl: 然之系统的基本url http://【localhost:808】/ranzhi/www
        """

        self.baseUrl = baseUrl
        self.driver = driver

    def openPage(self, url):
        """
        打开然之系统的页面,通过拼接URL的方式
        :param url: /sys/index.html
        :return:
        """
        self.driver.navigate(self.baseUrl + url)

3.Sub Page类

from ranzhiWeekend.ranzhi_base_page import RanzhiBasePage


class RanzhiSubLoginPage(RanzhiBasePage):
    def __init__(self, driver, baseUrl):
        """

        :param driver:
        :param baseUrl:
        """
        # 调用其 基类 RanzhiBasePage的 构造函数
        # 实现 基类 的构造函数的功能
        super().__init__(driver, baseUrl)
        self.loginPageUrl = "/sys/user-login.html"
        self.mainPageUrl = "/sys/index.html"
        self.driver.clearCookies()

    def login(self, userName, password):
        self.openPage(self.loginPageUrl)
        # self.driver.clearCookies()
        self.driver.implicitlyWait(5)
        self.driver.type("account", userName)
        self.driver.type("password", password)
        self.driver.click("submit")

    def getMainPage(self):
        return self.baseUrl + self.mainPageUrl

4.Tests Case 类

import unittest
from time import sleep

from ranzhiWeekend.automate_driver import AutomateDriver
from ranzhiWeekend.ranzhi_sub_login_page import RanzhiSubLoginPage

"""
1. 导入 unittest
2. 继承 unittest.TestCase
3. 写用例 方法以 test 开头
4. 考虑使用 setUp() 和 tearDown()
"""


class RanzhiTests(unittest.TestCase):
    def setUp(self):
        """
        开始每个测试前的准备事项
        :return:
        """
        self.autoDriver = AutomateDriver()
        self.baseUrl = "http://localhost:808/ranzhi/www"

    def tearDown(self):
        """
        结束每个测试后的清理工作
        :return:
        """
        self.autoDriver.quitBrowser()

    def test_ranzhi_login(self):
        """
        测试用例:测试然之登录
        :return:
        """
        # 新建然之的页面对象
        loginPage = RanzhiSubLoginPage(self.autoDriver, self.baseUrl)

        # 利用然之的页面对象进行登录
        loginPage.login("admin", "admin")
        sleep(2)
        # 断言 是否登录成功
        self.assertEqual(loginPage.getMainPage(), self.autoDriver.getUrl(), u"登录失败")

5.Tests Runner类

import smtplib
import unittest
from email.header import Header
from email.mime.text import MIMEText

from ranzhiWeekend import HTMLTestRunner
from ranzhiWeekend.ranzhi_tests_0605 import RanzhiTests


class RanzhiTestRunner():

    def runTest(self):
        """
        运行测试用例
        :return:
        """

        # 声明一个测试套件
        suite = unittest.TestSuite()
        # 添加测试用例到测试套件
        suite.addTest(RanzhiTests("test_ranzhi_login"))

        # 创建一个新的测试结果文件
        buf = open("./result.html", "wb")

        # 声明测试运行的对象
        runner = HTMLTestRunner.HTMLTestRunner(stream=buf,
                                               title="Ranzhi Test Result",
                                               description="Test Case Run Result")
        # 运行测试,并且将结果生成为HTML
        runner.run(suite)

        # 关闭文件输出
        buf.close()

    def sendEmail(self, targetEmail):
        """
        发送邮件
        :param targetEmail:
        :return:
        """

        # 打开测试报告结果
        f = open("./result.html", "rb")

        # 将测试结果放到邮件的主体中
        mailBody = f.read()
        # 关闭测试结果的文件
        f.close()

        # 声明一个邮件对象,用刚刚得到的邮件主体
        msg = MIMEText(mailBody, "html", "utf-8")
        # 设置邮件的主题
        msg["subject"] = Header("Automation Test Result", "utf-8")

        # 创建一个SMTP服务对象
        # simple message transfer protocol
        # 简单的消息转移协议
        smtpMail = smtplib.SMTP()

        # 连接SMTP的服务器
        smtpMail.connect(“***.******.com")

        # 登录SMTP的服务器
        smtpMail.login(“*******@*****.com", “*********")

        # 使用SMTP的服务器发送邮件
        smtpMail.sendmail(“*******@********.com", targetEmail, msg.as_string())

        # 退出SMTP对象
        smtpMail.quit()

6.main函数入口

if __name__ == "__main__":
    # 实例化一个runner
    runner = RanzhiTestRunner()

    # 执行测试
    runner.runTest()

    # 发送测试结果
    runner.sendEmail(“********@******.com")

(ctrl+c ctrl+v 立师兄Linty)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值