51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 771|回复: 0
打印 上一主题 下一主题

[转贴] UI自动化测试最佳设计模式POM,不会还不知道吧

[复制链接]
  • TA的每日心情
    擦汗
    昨天 09:05
  • 签到天数: 1048 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-4-21 09:44:50 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
    什么是POM?
      POM是Page Object Module(页面对象模式)的缩写,其目的是为了WebUI测试创建对象库。
      在这种模式下,应用涉及的每一个页面应该定义为一个单独的类,类中应该包含此页面上的页面元素对象和处理这些元素对象所需要的方法等。
      将流程所关联的页面作为对象,将对象串联起来,形成一个个不同的流程,POM是业内公认最佳的设计模式。
      POM的优点
      POM提供了页面元素操作和业务流程相分离的模式,可以使测试的代码结构比之前清晰,可读性强,更方便地复用对象和方法。
      对象库是独立于测试用例的、统一的对象库,可以通过集成不同的工具类来达到不同的测试目的。比如集成UnitTest可以用来做单元测试、自动化/功能测试,同时也可以集成JBehave/Cucumber等来做验收测试。
      使得整体自动化测试的优点变得更容易一些,如果有某个页面的元素需要变更,那么就可以直接更改封装好的页面元素类即可,而不用更改调用它的其他测试类/代码。这样整个的代码维护成本也会缩减。
      PO的核心就是分层思想,把同属于一个页面的元素都放在一个页面类中。
      简要结构设计
      逻辑代码
      基类,实现所有工具函数封装,类似于关键字驱动设计模式。
      页面对象代码
      基于系统页面,通过调用工具函数来实现业务的操作,生成对应的页面对象。
      测试代码
      基于测试需要,组件页面对象,实现核心流程的自动化,执行测试用例。
      测试数据
      为测试用例的执行提供所需要的测试数据。
      POM完整框架
      Base层
      定义项目需要的基础方法,特别是一些基础操作,如元素click操作、sendkeys操作,调用JavaScript脚本的方法和其他一些与基本浏览器相关的操作。
      Common层
      包含处理Excel文件的方法,获取项目路径、测试系统URL的信息和框架执行相关日志功能的实现方法。
      Data层
      存放测试数据,在这里可以维护测试数据,这样存放是为了让项目的可维护性强、整体的条理性强。测试数据有时是自动化测试的驱动因素,因此对Data的管理和维护就显得特别重要。
      Logs层
      存放项目在运行过程中产生的日志文件。
      PageObject层
      这里是PO的核心层,该层不但涉及代码技术,还涉及对项目业务的分析,进而对项目的页面进行分析。
      Reports层
      存放项目执行过程中产生的测试报告文件,测试报告是对测试结果的总结。
      TestCase层
      管理测试用例和执行测试,相当于测试的总入口。
      config.ini
      整个项目需要用到的配置项。
      项目实战
      以百度搜索为例:
      我们首先新建一个简单的项目结构:base、data、page_object、cases,如下图所示:

    image.png2. 在base包下新建一个base_page.py文件,在base_page.py文件下新建一个BasePage类作为基类,提供各个常用的已封装好的函数,便于后续的页面对象类进行调用。
      Selenium中常用的函数:元素定位、输入、点击、访问URL、等待、关闭等。代码如下:
    1. '''
    2.   基类:提供各个常用的已封装好的函数,便于后续的页面对象类进行调用。
    3.   selenium中常用的函数:元素定位、输入、点击、访问URL、等待、关闭
    4.   '''
    5.   from time import sleep
    6.   from selenium import webdriver
    7.   class BasePage:
    8.       driver = webdriver.Chrome()
    9.       # 访问URL
    10.       def visit(self, url):
    11.           self.driver.get(url)
    12.       # 元素定位
    13.       def locator(self, loc):
    14.           return self.driver.find_element(*loc)
    15.       # 输入
    16.       def input(self, loc, txt):
    17.           self.locator(loc).send_keys(txt)
    18.       # 点击
    19.       def click(self, loc):
    20.           self.locator(loc).click()
    21.       # 等待
    22.       def wait(self, time):
    23.           sleep(time)
    24.       # 关闭
    25.       def close(self):
    26.           self.driver.quit()
    复制代码
    在page_object包下新建一个search_page.py文件,在该文件下新建一个SearchPage类继承BasePage类,调用基础类中的方法实现页面操作,生成对应的页面对象。代码如下:

    1. '''
    2.   百度查询页面,搜索功能
    3.   '''
    4.   from selenium.webdriver.common.by import By
    5.   from base.base_page import BasePage
    6.   from selenium import webdriver
    7.   class SearchPage(BasePage):
    8.       url = 'http://www.baidu.com'
    9.       # 定位百度输入框
    10.       search_input = (By.NAME, 'wd')
    11.       # 定位百度一下按钮
    12.       search_button = (By.ID, 'su')
    13.       # 封装实现业务流程的函数
    14.       def search(self, txt):
    15.           self.visit(self.url)
    16.           self.wait(2)
    17.           self.input(self.search_input, txt)
    18.           self.wait(2)
    19.           self.click(self.search_button)
    复制代码
    在cases包下新建一个testcase.py文件作为测试类,实现在百度中分别搜索Selenium、Python,代码如下:
    1.   '''
    2.   测试类
    3.   '''
    4.   import unittest
    5.   from page_object.search_page import SearchPage
    6.   from selenium import webdriver
    7.   class Case(unittest.TestCase):
    8.       def test_search1(self):
    9.           driver = webdriver.Chrome()
    10.           txt = 'selenium'
    11.           SearchPage(driver).search(txt)
    12.           
    13.       def test_search2(self):
    14.           driver = webdriver.Chrome()
    15.           txt = 'python'
    16.           SearchPage(driver).search(txt)
    17.   if __name__ == '__main__':
    18.       unittest.main()
    复制代码
    运行以上代码发现,因为创建了两次WebDriver,所以每运行一次代码都会打开一个Chrome浏览器,同时,代码中也存在冗余。
      优化方案
      可以增加@classmethod作为前置条件和后置条件,搜索的条件也可以采用数据驱动的方式完成,提高代码的可读性与维护性。
      在data包下新建一个searchTXT.yaml文件,存放搜索的内容如下:

    优化之后代码如下:
    1. ‘’’
    2.   测试类
    3.   ‘’’
    4.   import unittest
    5.   from page_object.search_page import SearchPage
    6.   from selenium import webdriver
    7.   import ddt
    8.   @ddt.ddt()
    9.   class Case(unittest.TestCase):
    10.   @classmethod
    11.   def setUpClass(cls) -> None:
    12.       cls.driver = webdriver.Chrome()
    13.       cls.se = SearchPage(cls.driver)
    14.   @classmethod
    15.   def tearDownClass(cls) -> None:
    16.       cls.driver.quit()
    17.   @ddt.file_data('../data/searchTXT.yaml')
    18.   def test_search(self, txt):
    19.       self.se.search(txt)
    复制代码
    if name == ‘main’:
      unittest.main()
      运行以上代码,发现测试用例执行了四次,分别搜索了Selenium、Python、Java、php,如此实现了数据驱动,同时也在一个浏览器中完成了操作。
      后记
      以上简单的实现了POM的设计模式,完成了基类、页面对象层、数据层、测试层的代码编写,若读者有兴趣,可以完成报告层、日志层等内容的编写,最终实现完整的框架。
      敲字不易,如果此文章对你有帮助的话,点个赞收个藏来个关注,给作者一个鼓励。也方便你下次能够快速查找。




    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?(注-册)加入51Testing

    x
    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-19 01:55 , Processed in 0.071976 second(s), 25 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表