51Testing软件测试论坛

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

QQ登录

只需一步,快速开始

微信登录,快人一步

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

[转贴] PO模式在selenium自动化测试框架有什么好处

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

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2022-4-21 10:06:59 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    PO模式是在UI自动化测试过程当中使用非常频繁的一种设计模式,使用这种模式后,可以有效的提升代码的复用能力,并且让自动化测试代码维护起来更加方便。
      PO模式的全称叫page object model(POM),有时候叫做 page object pattern。最开始由马丁福勒提出,这个模式受到selenium自动化测试框架大力推广,因而成为一种非常主流的自动化测试设计模式。
      在PO模式当中,每一个UI页面使用编程语言当中的类来表示。在这个类当中,通过函数形式定义页面的行为和操作。这让调用方不需要关注具体执行的操作到底是点击还是拖动,而是关注具体的业务,比如登录、购物等等,甚至如果程序员直接把代码给产品经理看,他也是能看懂的。

     没有使用PO模式时
      在测试用例中直接编写浏览器操作API,对于代码编写者并没有多高的难度,因为他自己已经对这些API非常熟悉,但是这些浏览器操作并不能体现业务,至少没有产品经理那么熟悉,因此他很难和产品经理进行沟通,也难和开发沟通,甚至在半个月之后,他已经忘记了自己到底写了什么东西。
    1.  def test_login_mail(self):
    2.       driver = self.driver
    3.       driver.get("http://www.xxx.xxx.com")
    4.       driver.find_element_by_id("idInput").clear()
    5.       driver.find_element_by_id("xxxxxxx").send_keys("xxxxx")
    6.       driver.find_element_by_id("xxxxxxx").clear()
    7.       driver.find_element_by_id("xxxxxxx").send_keys("xxxxxx")
    8.       driver.find_element_by_id("loginBtn").click()
    复制代码
    使用PO模式
      使用PO模式有利于梳理业务,也有利于和其他人进行沟通。当你把下面这段代码拿给产品经理看的时候,他也大概能知道你测的是什么业务,能帮你纠正你的测试流程是否正确,或者提出一些更有建设性的意见,这对于大型项目需要频繁沟通和梳理业务时非常有用。
    1.  def test_login_mail(self):
    2.       LoginPage(driver).login()
    复制代码
    而浏览器本身的操作,就会被分离到一个更底层的模块,这些代码你可以不对调用方暴露,产品经理并不关心你这个页面中什么元素定位,他也不懂。

    1.  class LoginPage:
    2.   username_loc=(By.ID,"idInput")
    3.       password_loc =(By.ID,"pwdInput")
    4.       submit_loc =(By.ID,"loginBtn")
    5.       span_loc=(By.CSS_SELECTOR,"div.error-tt>p")
    6.       dynpw_loc =(By.ID,"lbDynPw")
    7.       userid_loc =(By.ID,"spnUid")
    8.      
    9.      
    10.       def __init__(self, driver):
    11.           self.driver = driver
    12.           
    13.       def login(self):
    14.           self.driver.find_element(*self.username_loc).clear()
    15.           self.driver.find_element(*self.username_loc).send_keys("xxxxx")
    16.           self.driver.find_element(*self.password_loc).clear()
    17.           self.driver.find_element(*self.password_loc).send_keys("xxxxxx")
    18.           self.driver.find_element(*self.submit_loc).click()
    复制代码
    这种方式把元素定位方式也分离了。但是这种元素定位的表达式可读性也不是很强,可以换用 property 方式来表示元素,所有的元素统一放在一起,修改起来也比较方便。

    1.  class LoginPage:
    2.       def __init__(self, driver)
    3.       self.driver = driver
    4.           
    5.       @property
    6.       def username_element(self):
    7.           return self.driver.find_element('id', 'idInput')
    8.      
    9.       @property
    10.       def password_element(self):
    11.           return self.driver.find_element('id', 'pwdInput')
    12.      
    13.       @property
    14.       def submit_element(self):
    15.           return self.driver.find_element('id', 'loginBtn')
    16.      
    17.       def login(self, name, password):
    18.           self.username_element.send_keys(name)
    19.           self.password_element.send_keys(password)
    20.           self.submit_element.click()
    复制代码
    第三种方式可以充分利用Python的描述符特性,你会发现很多序列化库或者ORM框架都有类似的用法。
    1. class LoginPage:
    2.       def __init__(self, driver)
    3.       self.driver = driver
    4.           
    5.       username = Element(css='#idInput', desc='用户名输入框')
    6.       password = Element(css='#pwdInput', desc='密码输入框')
    7.       confirm = Element(css='#loginBtn', desc='登录确认按钮')
    8.      
    9.      def login(self, name, password):
    10.           self.username.send_keys(name)
    11.           self.password.send_keys(password)
    12.           self.confirm.click()
    复制代码
     而 Element 类可以通过 Python 描述符实现,这里为了方便,只定义了xpath的元素定位方法:
    1.  class Element:
    2.       def __init__(self,xpath=None,desc=''):
    3.           self.xpath = xpath
    4.           self.desc = desc
    5.       def __get__(self, instance, owner):
    6.           driver = instance.browser
    7.           el = driver.find_element('xpath', self.xpath)
    8.           return el
    复制代码
    PO模式和DDD
      PO模式是DDD(领域驱动设计)的一个简单实现,但是还不够彻底。如果要在自动化测试中贯彻DDD,我觉得还有一些可以优化的空间。
      首先某一个业务不一定只是单个页面的操作,比如登录不一定只涉及到LoginPage这个页面,因此直接在LoginPage中编写login函数就不是很合理。对于调用方来说,应该明确说明的是谁在登录,而不是指某个页面。像这样:
    1.  user.login()
    2.   # or
    3.   login(user)
    复制代码
    我们编写的代码就像是自然语言,任何懂英语的人都知道代码在做什么,在DDD中,叫做领域特定语言(DSL), 要实现这种逻辑,在Page类和调用中间应该还会有一个层级来封装user。
      其次,Page页面会依赖更底层的资源,比如组件,元素类型。因此在 Page 类的下方应该会使用 InputElement, ButtonElement 、SelectElement 这样的元素类和 HeaderComponent、FooterComponent 这样的组件类。
    1. class LoginPage:
    2.   username_filed = InputElement('xxx')
    3.   password_filed = PasswordElement('xxx')
    复制代码
    领域驱动设计对于大型项目梳理业务、同步业务、沟通业务是非常有帮助的,是一种以业务为中心的设计范式。PO模式对于DDD的小范围应用,以及具体了足够多的好处:
      ·便于维护。每一个页面的操作都被单独的存放在一个类文件中,当前端页面被修改之后,只需要找到对应类文件进行修改,其他的代码并不需要进行修改,这符合单一职责原则。
      · 便于重复使用。在进行自动化测试的时候,一个测试由多个测试步骤组成,这些测试步骤可能涉及到多个页面的操作。而用例与用例之间的操作可能重合。PO模式可以重复利用这些测试步骤,简化代码的编写。
      · 提高了可读性。页面的操作都被以函数的形式封装起来了。函数名就具备注释的作用,其他人阅读代码时可以通过函数了解业务。




    本帖子中包含更多资源

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

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

    使用道具 举报

    本版积分规则

    关闭

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

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

    GMT+8, 2024-5-7 01:41 , Processed in 0.058513 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

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