乐趣区

关于软件测试:不懂PO-设计模式这篇实战文带你搞定-PO

为 UI 页面写测试用例时(比方 web 页面,挪动端页面),测试用例会存在大量元素和操作细节。当 UI 变动时,测试用例也要跟着变动,PageObject 很好的解决了这个问题!

应用 UI 自动化测试工具时(包含 selenium,appium 等),如果无对立模式进行标准,随着用例的增多会变得难以保护,而 PageObject 让自动化脚本井井有序,将 page 独自保护并封装细节,能够使 testcase 更持重,不须要大改大动。

具体做法:把元素信息和操作细节封装到 Page 类中,在测试用例上调用 Page 对象(PageObject),比方存在一个性能“选取相册题目”,须要为之建设函数 selectAblumWithTitle(),函数外部是操作细节 findElementsWithClass(‘album’) 等:

以选“取相册题目”举例,伪代码如下:

selectAblumWithTitle() {
    #选取相册
    findElementsWithClass('album')
    #选取相册题目
    findElementsWithClass('title-field')
    #返回题目内容
    return getText()}

page object 的次要准则是提供一个简略接口 (或者函数,比方上述的 selectAblumWithTitle),让调用者在页面上能够做任何操作,点击页面元素,在输入框输出内容等。

因而,如果要拜访一个文本字段,page object 应该有获取和返回字符串的办法。page object 应该封装对数据的操作细节,比方查找元素和点击元素。当页面元素改变时,应该只扭转 page 类中的内容,不须要扭转调用它的中央。

不要为每个 UI 页面都创立一个 page 类,应该只为页面中重要的元素创立 page 类。

比方,一个页面显示多个相册,应该创立一个相册列表 page object,它蕴含许多相册 page object。如果某些简单 UI 的层次结构只是用来组织 UI,那么它就不应该呈现在 page object 中。page object 的目标是通过给页面建模,从而对应用程序的使用者变得有意义:

如果你想导航到另一个页面,初始 page 对象该当 return 另一个 page 对象,比方点击注册,进入注册页面,在代码中就应该 return Register()。如果想获取页面信息,能够 return 根本类型 ( 字符串、日期)。

倡议不要在 page object 中放断言。应该去测 page object,而不是让 page object 本人测本人,page object 的责任是提供页面的状态信息。这里仅用 HTML 形容 Page Object,这种模式还能够用来暗藏 Java swing UI 细节,它可用于所有 UI 框架。

PageObject 的核心思想是六大准则,把握六大准则才能够进行 PageObject 实战演练,这是 PageObject 的精华所在。
selenium 官网凝聚出六大准则,前面的 PageObject 应用都将围绕六大准则发展:

  • 公共办法代表页面提供的服务
  • 不要裸露页面细节
  • 不要把断言和操作细节混用
  • 办法能够 return 到新关上的页面
  • 不要把整页内容都放到 PO 中
  • 雷同的行为会产生不同的后果,能够封装不同后果

上面,对上述六大准则进行解释:

  • 准则一:要封装页面中的性能(或者服务),比方点击页面中的元素,能够进入到新的页面,于是,能够为这个服务封装办法“进入新页面”。
  • 准则二:封装细节,对外只提供办法名(或者接口)。
  • 准则三:封装的操作细节中不要应用断言,把断言放到独自的模块中,比方 testcase。
  • 准则四:点击一个按钮会开启新的页面,能够用 return 办法示意跳转,比方 return MainPage() 示意跳转到新的 PO:MainPage。
  • 准则五:只为页面中重要的元素进行 PO 设计,舍弃不重要的内容。
  • 准则六:一个动作可能产生不同后果,比方点击按钮后,可能点击胜利,也可能点击失败,为两种后果封装两个办法,click_success 和 click_error。

以企业微信首页为例,企业微信首页有二个次要性能:立刻注册和企业登录。
企业微信网址:https://work.weixin.qq.com/

点击企业登录能够进入登录页面,在页面能够扫码登录和企业注册。

点击企业注册能够进入注册页面,在页面能够输出相干信息进行注册。

用 page object 准则为页面建模,这里波及三个页面: 首页,登录,注册。在代码中创立对应的三个类 Index,Login,Register:
• 登陆页⾯提供 login findPassword 性能
– Login 类 + login findPassword ⽅法
• 登录页⾯内的元素有多少并不关⼼,暗藏外部界⾯控件
• 登录胜利和失败会别离返回不同的页⾯
– findPassword
– loginSuccess
– loginFail
• 通过⽅法返回值判断登录是否合乎预期

BasePage 是所有 page object 的父类,它为子类提供公共的办法,比方上面的 BasePage 提供初始化 driver 和退出 driver,代码中在 base_page 模块的 BasePage 类中应用__init__初始办法进行初始化操作,包含 driver 的复用,driver 的赋值,全局期待的设置(隐式期待)等等:

from time import sleep
from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver


class BasePage:
    def __init__(self, driver: WebDriver = None):
        #此处对 driver 进行复用,如果不存在 driver, 就结构一个新的
        if driver is None:
            # Index 页面须要用,首次应用时结构新 driver
            self._driver = webdriver.Chrome()
            # 设置隐式等待时间
            self._driver.implicitly_wait(3)
            # 拜访网页
            self._driver.get(self._base_url)
        else:
            # Login 与 Register 等页面须要用这个办法,防止反复结构 driver
            self._driver = driver

    def close(self):
        sleep(20)
        self._driver.quit()

Index 是企业微信首页的 page object,它存在两个办法,进入注册 page object 和进入登录 page object,这里 return 办法返回 page object 实现了页面跳转,比方:goto_register 办法 return Register,实现从首页跳转到注册页:

class Index(BasePage):
    _base_url = "https://work.weixin.qq.com/"
    # 进入注册页面
    def goto_register(self):
        self._driver.find_element(By.LINK_TEXT, "立刻注册").click()
        # 创立 Register 实例后,可调用 Register 中的办法
        return Register(self._driver)
    # 进入登录页面
    def goto_login(self):
        self._driver.find_element(By.LINK_TEXT, "企业登录").click()
        # 创立 Login 实例后,可调用 Login 中的办法
        return Login(self._driver)

Login 是登录页面的 page object,次要性能有:进入注册页面,扫描二维码,因而创立两个办法代表两个性能:scan_qrcode 和 goto_registry。代码跟下面类似,不过多介绍:

from selenium.webdriver.common.by import By
from test_selenium.page.base_page import BasePage
from test_selenium.page.register import Register

class Login(BasePage):
    # 扫描二维码
    def scan_qrcode(self):
        pass
    # 进入注册页面
    def goto_registry(self):
        self._driver.find_element(By.LINK_TEXT, "企业注册").click()
        return Register(self._driver)

Register 是注册页面的 page object,次要性能是填写正确注册信息,当填写谬误时,返回错误信息。register 办法实现了正确的表格填写,当填写结束时返回本身(页面还停留在注册页)。get_error_message 办法实现了谬误填写的状况,如果填写谬误,就收集谬误内容并返回:

from selenium.webdriver.common.by import By
from test_selenium.page.base_page import BasePage


class Register(BasePage):
    # 填写注册信息,此处只填写了局部信息,并没有填写齐全
    def register(self, corpname):
        # 进行表格填写
        self._driver.find_element(By.ID, "corp_name").send_keys(corpname)
        self._driver.find_element(By.ID, "submit_btn").click()
        # 填写结束,停留在注册页,可持续调用 Register 内的办法 
        return self
    #填写谬误时,返回错误信息
    def get_error_message(self):
        # 收集错误信息并返回
        result=[]
        for element in self._driver.find_elements(By.CSS_SELECTOR, ".js_error_msg"):
            result.append(element.text)

        return result

test_index 模块是对上述性能的测试,它独立于 page 类,在 TestIndex 类中只须要调用 page 类提供的办法即可,比方上面对注册页及登陆页的测试应用了 test_register 和 test_login 办法:

from test_selenium.page.index import Index


class TestIndex:
    # 所有步骤前的初始化
    def setup(self):
        self.index = Index()
    # 对注册性能的测试
    def test_register(self):
        # 进入 index,而后进入注册页填写信息
        self.index.goto_register().register("霍格沃兹测试学院")
    # 对 login 性能的测试
    def test_login(self):
        # 从首页进入到注册页
        register_page = self.index.goto_login().goto_registry()\
            .register("测吧(北京)科技有限公司")
        # 对填写后果进行断言,是否填写胜利或者填写失败
        assert "请抉择" in "|".join(register_page.get_error_message())
    # 敞开 driver
    def teardown(self):
        self.index.close()

退出移动版