自动化之PO模式(二)

1.PO基本介绍

测试PO模式(Page Object Model)

  测试页面和测试脚本分离,即页面封装成类,供测试脚本进行调用。

优点

    1.提高测试用例的可读性;

    2.减少了代码的重复;

    3.提高测试用例的可维护性,特别是针对UI频繁变动的项目;

缺点

    结构复杂: 基于流程做了模块化的拆分。

PO:page objece,分层机制,让不同层去做不同类型的事情,让代码结构清晰,增加复用性。
主要有以下几种分层方式:
1)两层: 对象逻辑层+业务数据层
2)三层:对象库层+逻辑层+业务数据层
3)四层:对象库层+逻辑层+业务层+数据层

优势:
1)效率高 :同理,PO模式的逻辑层方法有具体定义,情况和元素发生变化一样 修改逻辑层,业务层不变。这样看来结构简单清晰,舒服更符合人类习惯, 普通方式就是继续堆case。
2) 复用多收益大: 同样这里如果逻辑复用越多,PO模式收益越大,因为对于PO模式来说都只需要修改一个地方多处受益。
 

2.Appium方法二次封装

为什么二次封装方法?

    减少过多使用类似方法,更方便管理定位元素,尽量将函数方法简洁化、统一化。

系统已提供定位方法

  driver.find_element_by_id()

  driver.find_element_by_class_name()

  driver.find_element_by_xpath()

  driver.find_elements_by_id()

  driver.find_elements_by_class_name()

  driver.find_elements_by_xpath()

  实际以上定位方法封装以下方法:

      find_element(by=By.XX, value=None)

      find_elements(by=By.XX, value=None)

      # by:定位类型(By.ID,By.CLASS_NAME,By.XPATH)

      # value:定位元素的属性值

封装思路

为什么做?

  使用统一的方法来完成元素的定位.

怎么去做?

  借用:

      find_element(by=By.XX, value=None)

      find_elements(by=By.XX, value=None)

封装实现(一)

  # 点击元素

  def click_element(self, location,location_vlaue):

      # location: 定位类型

      # location_vlaue: 定位元素属性值

      self.find_element(location,location_vlaue).click()

  # 输入内容

  def input_element(self, location,location_vlaue,text):

      # text: 需要输入的值

      self.find_element(location,location_vlaue).send_keys(text)

  发现问题:

      没有简化参数的传递,仅仅将点击和输入做了封装.

封装实现(二)

  # 点击元素

  def click_element(self, location):

      # location: 定位类型&定位属性值,

      ⚠️ location类型为元祖 格式(By.ID,value),(By.CLASS_NAME,value),(By.XPATH,value)

      self.find_element(*location).click()

      # *location将元祖类型的值进行了一次解包操作,来满足find_element的参数传递要求

  # 输入内容

  def input_element(self, location,text):

      # text: 需要输入的值

      self.fm = self.find_element(*location)

      self.fm.clear() # 需要先清空输入框,防止有默认内容

      self.fm.send_keys(text)

Demo示例

  业务场景:

      1.进入设置

      2.点击搜索按钮

      3.输入123

      4.点击搜索框返回按钮

  封装代码:Base.py

      from selenium.webdriver.support.wait import WebDriverWait

      class Base(object):

          def __init__(self,driver):

              self.driver = driver

          def find_element(self,loc,timeout=10):

              # 封装称为智能等待方法

              # loc:类型为元祖,格式(By.ID,value),(By.CLASS_NAME,value),(By.XPATH,value)

              # timeout:搜索超时时间

              return WebDriverWait(self.driver, timeout).until(lambda x: x.find_element(*loc))

          def click_element(self,loc):

              # 封装点击操作

              self.find_element(loc).click()

          def input_text(self,loc,text):

              # 封装输入操作

              self.fm = self.find_element(loc)

              self.fm.clear() # 需要先清空输入框,防止有默认内容

              self.fm.send_keys(text)

  测试代码: test.py

      from appium import webdriver

      from selenium.webdriver.common.by import By

      # 导入已封装的类 Base.py

      from Base import Base

      class Test_Base:

          def __init__(self):

              desired_caps = {}

              # 手机 系统信息

              desired_caps['platformName'] = 'Android'

              desired_caps['platformVersion'] = '5.1'

              # 设备号

              desired_caps['deviceName'] = '192.168.56.101:5555'

              # 包名

              desired_caps['appPackage'] = 'com.android.settings'

              # 启动名

              desired_caps['appActivity'] = '.Settings'

              # 允许输入中文

              desired_caps['unicodeKeyboard'] = True

              desired_caps['resetKeyboard'] = True

              # 手机驱动对象

              self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

          def test(self):

              # 搜索按钮

              search_button = (By.ID,"com.android.settings:id/search")

              # 搜索输入框

              search_text = (By.ID,"android:id/search_src_text")

              # 搜索框返回按钮

              search_return_button = (By.CLASS_NAME,"android.widget.ImageButton")

              # 实例化二次封装的Base类

              base_object = Base(self.driver)

              # 点击搜索输入框

              base_object.click_element(search_button)

              # 搜索框内输入123

              base_object.input_text(search_text,123)

              # 点击搜索框返回按钮

              base_object.click_element(search_return_button)

              # 退出driver对象

              self.driver.quit()

      if __name__ == "__main__":

          Test_Base().test()

3.页面元素操作封装(一)

    将页面元素定位和元素操作封装在一个类文件中.

封装步骤

确定好要封装的页面操作

  业务场景:

      1.进入设置

      2.点击搜索按钮

      3.输入123

      4.点击搜索框返回按钮

需要用到哪些元素和定位方法

  # 搜索按钮

  search_button = (By.ID, "com.android.settings:id/search")

  # 搜索输入框

  search_text = (By.ID, "android:id/search_src_text")

  # 搜索框返回按钮

  search_return_button = (By.CLASS_NAME,"android.widget.ImageButton")

依赖的定位方法

  使用二次封装的方法,Base.py文件

页面封装

  编写搜索操作类:search_page.py

      from selenium.webdriver.common.by import By

      # 导入基础定位封装包

      from Base import Base

      class Search_Page:

          def __init__(self,driver):

              # 传如driver对象

              self.driver = driver

              # 实例化二次封装的类,用到封装好的操作函数

              self.base_object = Base(self.driver)

              # 搜索按钮

              self.search_button = (By.ID, "com.android.settings:id/search")

              # 搜索输入框

              self.search_text = (By.ID, "android:id/search_src_text")

              # 搜索框返回按钮

              self.search_return_button = (By.CLASS_NAME,"android.widget.ImageButton")

          def input_search_text(self,text):

              # text: 需要输入的内容

              # 封装搜索按钮的输入操作

              # 点击设置中搜索按钮

              self.base_object.click_element(self.search_button)

              # 在搜索输入框内输入123

              self.base_object.input_text(self.search_text,text)

              # 点击搜索框返回按钮

              self.base_object.click_element(self.search_return_button)

  编写测试文件: test.py

      from appium import webdriver

      # 导入封装好的页面类

      from search_page import Search_Page

      class Test_Base:

          def __init__(self):

              desired_caps = {}

              # 手机 系统信息

              desired_caps['platformName'] = 'Android'

              desired_caps['platformVersion'] = '5.1'

              # 设备号

              desired_caps['deviceName'] = '192.168.56.101:5555'

              # 包名

              desired_caps['appPackage'] = 'com.android.settings'

              # 启动名

              desired_caps['appActivity'] = '.Settings'

              # 允许输入中文

              desired_caps['unicodeKeyboard'] = True

              desired_caps['resetKeyboard'] = True

              # 手机驱动对象

              self.driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

          def test(self):

              # 实例化页面封装类

              sp = Search_Page(self.driver)

              # 调用操作类

              sp.input_search_text(123)

              # 退出driver对象

              self.driver.quit()

      if __name__ == "__main__":

          Test_Base().test()

问题

  1.若我要完成Base基础类的导入,需要在每个页面的封装类内实例化Base类,

      并需要实例化对象引用Base类内部方法。

      即:

          self.driver = driver

          self.base_object = Base(self.driver)

  2.若我们有多个测试函数文件,需要在每个文件中都声明一次driver,

      重复代码太多,并且测试类内部冗余代码过多。

4.页面元素操作封装(二)

    通过类继承的方式完成页面的封装

页面封装

  业务场景:

      1.进入设置

      2.点击搜索按钮

      3.输入123

      4.点击搜索框返回按钮

  页面封装类:search_page.py

      from selenium.webdriver.common.by import By

      # 导入基础定位封装包

      from Base import Base

      class Search_Page(Base):

          def __init__(self,driver):

              Base.__init__(self,driver) # 父类初始化方法

              # 搜索按钮

              self.search_button = (By.ID, "com.android.settings:id/search")

              # 搜索输入框

              self.search_text = (By.ID, "android:id/search_src_text")

              # 搜索框返回按钮

              self.search_return_button = (By.CLASS_NAME,"android.widget.ImageButton")

          def input_search_text(self,text):

              # text: 需要输入的内容

              # 封装搜索按钮的输入操作

              # 点击设置中搜索按钮

              self.click_element(self.search_button) # 子类继承父类的所有方法

              # 在搜索输入框内输入

              self.input_text(self.search_text,text)

              # 点击搜索框返回按钮

              self.click_element(self.search_return_button)

独立手机驱动对象方法

  统一管理手机驱动对象的初始化

    新建手机驱动对象文件:Init_Driver.py

    代码:

        from appium import webdriver

        def init_driver():

            # 服务端启动参数

            desired_caps = {}

            # 手机 系统信息

            desired_caps['platformName'] = 'Android'

            desired_caps['platformVersion'] = '5.1'

            # 设备号

            desired_caps['deviceName'] = '192.168.56.101:5555'

            # 包名

            desired_caps['appPackage'] = 'com.android.settings'

            # 启动名

            desired_caps['appActivity'] = '.Settings'

            # 允许输入中文

            desired_caps['unicodeKeyboard'] = True

            desired_caps['resetKeyboard'] = True

            # 手机驱动对象

            driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

            return driver # 返回driver对象

测试脚本编写

    代码实现: test.py

        # 导入封装好的页面类

        from cup.search_page import Search_Page

        # 导入独立的手机驱动对象

        from cup.Init_Driver import init_driver

        class Test_Base:

            def __init__(self):

                self.driver = init_driver()

            def test(self):

                # 示例化页面封装类

                sp = Search_Page(self.driver)

                # 调用操作类

                sp.input_search_text(123)

                # 退出driver对象

                self.driver.quit()

        if __name__ == "__main__":

            Test_Base().test()

5.PO模式项目管理

目录结构

    清晰的目录结构,方便代码模块化管理和他人解读。

    常用目录结构:

        App_Project # 项目名称

          - Basic # 存储基础设施类

              - __init__.py # 空文件

              - Init_Driver.py # 手机驱动对象初始化

              - Base.py # 方法的二次封装

              - read_data.py #数据解析读取方法

          - Page # 存储封装页面文件

              - __init__.py # 存储页面元素

              - search_page.py # 封装页面元素的操作方法

          - Data # 存储数据文件

              - search_data.yaml(也可以是其他文件比如txt,excel,json,数据库等)

          - Test # 存储测试脚本目录

              - test_search.py # 测试搜索文件

Basic目录文件代码

    1.Basic/Init_Driver.py

    from appium import webdriver

    def init_driver():

        # 服务端启动参数

        desired_caps = {}

        # 手机 系统信息

        desired_caps['platformName'] = 'Android'

        desired_caps['platformVersion'] = '5.1'

        # 设备号

        desired_caps['deviceName'] = '192.168.56.101:5555'

        # 包名

        desired_caps['appPackage'] = 'com.android.settings'

        # 启动名

        desired_caps['appActivity'] = '.Settings'

        # 允许输入中文

        desired_caps['unicodeKeyboard'] = True

        desired_caps['resetKeyboard'] = True

        # 手机驱动对象

        driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub", desired_caps)

        return driver

    2.Basic/Base.py

    from selenium.webdriver.support.wait import WebDriverWait

    class Base(object):

        def __init__(self,driver):

            self.driver = driver

        def find_element(self,loc,timeout=10):

            '''

                二次封装find_element方法,增加了显示等待和简化参数传递

            '''

            # 原生方法:find_element(by=By.ID, value=None),需要传递两个值

            # *loc 将传入(By.XX, "value")解包为两个单独的值,满足find_element方法的参数传递

            return WebDriverWait(self.driver, timeout).until(lambda x: x.find_element(*loc))

        def click_element(self,loc):

            '''

                封装点击操作函数

            '''

            self.find_element(loc).click()

        def input_text(self,loc,text):

            '''

                封装输入操作函数

            '''

            self.fm = self.find_element(loc)

            self.fm.clear() # 需要先清空输入框,防止有默认内容

            self.fm.send_keys(text)

Page目录文件代码

    1.Page/__init__.py

    from selenium.webdriver.common.by import By # selenium原生定位策略集

    '''

        设置页面

    '''

    # 搜索按钮

    search_button = (By.ID, "com.android.settings:id/search")

    # 搜索输入框

    search_text = (By.ID, "android:id/search_src_text")

    # 搜索框返回按钮

    search_return_button = (By.CLASS_NAME,"android.widget.ImageButton")

    2.Page/search_page.py

    # 导入基础定位封装包

    from Basic.Base import Base

    import Page

    class Search_Page(Base):

        def __init__(self,driver):

            Base.__init__(self,driver) # Base类的初始化方法

        def input_search_text(self,text):

            '''

                封装搜索按钮的输入操作

            '''

            # 点击设置中搜索按钮

            self.click_element(Page.search_button) # 传入的__init__.py文件声明的search_button变量

            # 在搜索输入框内输入

            self.input_text(Page.search_text,text) # 传入的__init__.py文件声明的search_text变量

            # 点击搜索框返回按钮

            self.click_element(Page.search_return_button) # 传入的__init__.py文件声明的search_return_button变量

Test目录文件代码

    1.test_search.py

    # 导入封装好的页面类

    from Page.search_page import Search_Page

    # 导入独立的手机驱动对象

    from Basic.Init_Driver import init_driver

    import time

    class Test_Base:

        def __init__(self):

            self.driver = init_driver()

        def test(self):

            # 示例化页面封装类

            sp = Search_Page(self.driver)

            # 调用操作类

            sp.input_search_text(123)

            # 退出driver对象

            self.driver.quit()

    if __name__ == "__main__":

        Test_Base().test()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值