为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 sleepfrom selenium import webdriverfrom selenium.webdriver.remote.webdriver import WebDriverclass 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 Byfrom test_selenium.page.base_page import BasePagefrom test_selenium.page.register import Registerclass 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 Byfrom test_selenium.page.base_page import BasePageclass 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 Indexclass 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()