Selenium之Page Object基础知识

Page Object是UI自动化测试项目开发实践的最佳实践之一,在接口交互的详细包中体现出主要特征,将测试用例集中于业务操作,提高测试用例的维护性。

一、认识Page Object

当为web页面编写测试时,需要操作网页上的元素。但是,如果在测试代码中直接操作web页面上的元素,那么这样的代码是极其脆弱的,因为UI会经常变化。
page对象的一个基本经验法则是,任何人都可以通过软件客户端完成page对象。因此,必须提供易于编程的界面,并隐藏窗口底部的部件。访问文件箱时,需要使用访问方法( Accessor Method )获取和返回字符串。 复选框必须使用布尔值,按钮必须以贝壳形式显示为指导方法名称。 page对象必须将GUI控件上的所有查询和操作数据的行为封装为方法。
一个好的经验法则是,即使改变具体的元素,page对象的接口也不应当发生变化。
虽然这个术语是page对象,但并不意味着每个页面都必须创建这样的对象。例如,页面上具有重要意义的元素可以独立为一个page对象。 经验法则的目的是通过对页面进行建模,使应用程序的使用者有意义。
Page Object是一种设计模式,在自动化测试开发中应遵循这种设计模式来编写代码。
Page Object应该遵循以下原则进行开发:

  1. Page Object应该易于使用
  2. 有清晰的结构,如PageObjects对应页面对象,PageModules对应页面内容
  3. 只写测试内容,不写基础内容
  4. 在可能的情况下防止样板代码
  5. 不需要自己管理浏览器
  6. 在运行时选择浏览器,而不是类级别
  7. 不需要直接接触Selenium

二、实现Page Object

下面我们将通过例子介绍这种设计模式的使用
2.1 Paget Object简单实例:
以百度搜索为例,假设我们有如下测试代码:

...
def test_baidu_search_casel(self):
    self.driver.get(self.base_url)
    self.driver.find_element_by_id("kw").send_keys("selenium")
    self.driver.find_element_by_id("su").click()
    
def test_baidu_search_casel(self):
    self.driver.get(self.base_url)
    self.driver.find_element_by_id("kw").send_keys("unittest")
    self.driver.find_element_by_id("su").click()

def test_baidu_search_casel(self):
    self.driver.get(self.base_url)
    self.driver.find_element_by_id("kw").send_keys("Page object")
    self.driver.find_element_by_id("su").click()
...    

这段代码最大的问题是元素的定位和操作在三个测试用例中被重用。这会带来一个大问题。当一个元件的定位发生变化时,例如id=kw失效,应及时调整定位方法。此时需要在三个测试用例中进行修改。假设我们的自动化项目中有上百个测试用例,UI很可能会频繁变化,这样会增加自动化测试用例的维护成本。
Page Object的设计思想最直接的优点是,通过将元素放置在元素操作中进行分层,在元素发生变化时,只需要保持page层的元素放置,而不需要关心在哪个测试用例中使用了这些元素。 在创建测试用例时,也不需要关心元素如何定位。
创建baidu_page.py文件,内容如下:

class BaiduPage():
    # 初始化驱动器
    def __init__(self, driver):
        self.driver = driver
    
    # 搜索输入框定位方法以及内容输入
    def search_input(self, search_key):
        # id定位
        self.driver.find_element_by_id("wk").send_keys(search_key)


    # 按钮的定位点击
    def search_button(self):
        self.driver.find_element_by_id("su").click()


首先创建BaiduPage类,在__init__()初始化方法中接收参数driver,分配给self.driver…然后,封装search_input()方法和search_button()方法,定位和操作元素。这里的封装只针对可以在页面上操作的元素。原则上,一个元素被封装到一个方法中。当一个元素的定位方法发生变化的时候,你只需要在这里维护方法,而不需要关心这个方法是在哪个测试用例中使用的。

from baidu_page import BaiduPage
...
    def test_baidu_search_case1(self):
        self.driver.get(self.base_url)
        bd = BaiduPage(self.driver)
        bd.search_input("selenium")
        bd.search_button()
    
    def test_baidu_search_case2(self):
        self.driver.get(self.base_url)
        bd = BaiduPage(self.driver)
        bd.search_input("unittest")
        bd.search_button()


    def test_baidu_search_case3(self):
        self.driver.get(self.base_url)
        bd = BaiduPage(self.driver)
        bd.search_input("page pbject")
        bd.search_button()


...

首先将BaiduPage类导入到测试中,然后将驱动引入到每个测试用例中,这样就很容易使用其封装的方法来设计具体的测试用例。这样做的目的是消除测试用例中的元素定位。想要操作百度输入框,只需要调用search_input()方法,传入搜索关键词,不需要关心百度输入框是怎么定位的。
2.2改进Paget Object封装
上例给出了Page Object设计模型的基本原理。这样的分层确实会带来不好的好处,但也存在一些问题。例如,需要写更多的代码。以前的测试用例只需要写4~5行代码,但是在特定的测试用例中引用之前,必须对Page层中操作的每个元素进行封装。为了便于封装Page层,进行了一些改进。
创建base.py文件,内容如下:

import time


class BasePage:
    """
    基础Page层,封装一些常用方法
    """


    def __init__(self, driver):
        self.driver = driver
    
    # 打开页面
    def open(self, url=None):
        if url is None:
            self.driver.get(self.url)
        else:
            self.driver.get(url)
    
    # id 定位
    def by_id(self, id_):
        return self.driver.find_element_by_id(id_)
    
    # name 定位
    def by_name(self, name):
        return self.driver.find_element_by_name(name)


    # XPath 定位
    def by_xpath(self, xpath):
        return self.driver.find_element_by_xpath(xpath)
    
    # CSS 定位
    def by_css(self, css):
        return self.driver.find_element_by_css_selector(css)
    
    # 获取title
    def get_title(self):
        return self.driver.title
    
    # 获取页面text,仅使用XPath定位
    def get_text(self, xpath):
        return self.by_xpath(xpath).text


    # 执行JavaScript脚本
    def js(self, script):
        self.driver.excuye_script(script)

创建BasePage类作为所有Page类的基类,在BasePage类中封装一些方法,这些方法是我们在做自动化时经常用到的。
open()方法用于打开网页,它接收一个url参数,默认为None。如果url参数为None,则默认打开子类中定义的url。稍后会在子类中定义url变量。
by_id()和by_name()方法,我们知道,Selenium提供的元素定位方法很长,这里做了简化,只是为了在子类中使用更加简便。
get_title()和get_text()方法。这些方法是在写自动化测试时经常用到的方法,也可以定义在BasePage类中。需要注意的是,get_text()方法需要接收元素定位,这里默认为XPath定位。
当然,我们还可以根据自己的需求封装更多的方法到BasePage类中。
修改baidu_page.py文件

from base import BasePage


class BaiduPage(BasePage):
    """
    百度Page层,百度页面封装操作到的元素
    """
    url = "https://www.baidu.com"


    def search_input(self, search_key):
        self.by_id("kw").send_keys(search_key)


    def search_button(self):
        self.by_id("su").click()

创建BaiduPage.py类继承BasePage类,定义url变量,供父类中的open()方法使用。这里可能会有点绕,所以举个例子:
小明的父亲有一辆电动玩具汽车,电动玩具汽车需要电池才能跑起来,但小明的父亲并没有为电动玩具汽车安装电池。小明继承了父亲的这辆电动玩具汽车,为了让电动玩具汽车跑起来,小明购买了电池。在这个例子中,open()方法就是"电动玩具汽车",open()方法中使用的self.url就是"电池",子类中定义的url是为了给父类中的open()方法使用的。
在search_input()和search_buttom()方法中使用了父类的self.by_id()方法来定位元素,比原始的Selenium方法简短了不少。
在测试用例中,使用BaiduPage类及它所继承的父类中的方法。

import unittest
from time import sleep
from selenium import webdriver
from baidu_page import BaiduPage


class TestBaidu(unittest.TestCase):


    @classmethod
    def setUpClass(cls):
        cls.driver = webdriver.Chrome()
    
    def test_baidu_search_case(self):
        page = BaiduPage(self.driver)
        page.open()
        page.search_input("selenium")
        page.search_button()
        sleep(2)
        self.assertEqual(page.get_title(), "selenium_百度搜索")


    @classmethod
    def tearDownClass(cls):
        cls.driver.quit()


if __name__ == '__main__':
    unittest.main(verbosity=2)

因为前面封装了元素的定位,所以在编写测试用例时会方便不少,当需要用到哪个Page类时,只需将它传入浏览器驱动,就可以使用该类中提供的方法了。
2.3poium测试库
poium是一个基于Selenuim/appium的Page Object测试库,最大的特点是简化了Page层元素的定义。
项目地址: https://github.com/defnngj/poium
支持pip安装:pip install poium
3.1基本使用
使用poium重写baidu_page.py

from poium import Page, PageElement


class BaiduPage(Page):
    """
    百度Page层,百度页面封装操作的元素
    """
    search_input = PageElement(id_="kw")
    search_button = PageElement(id_="su")

创建BaiduPage类,使其继承poium库中的Page类。调用PageElement类定义元素定位,并赋值给变量search_input和search_button。这里仅封装元素的定位,并返回元素对象,元素的具体操作仍然在测试用例中完成,这也更加符合Page Object的思想,将元素定位与元素操作分层。
在测试用例中的使用如下。

from baidu_page import BaiduPage
class TestBaidu(unittest.TestCase):
    ...
    def test_baidu_search_case1(self):
        page = BaiduPage(self.driver)
        page.get("https://www.baidu.com")
        page.search_input = "selenium"
        page.search_button.click()
    
    ...

首先导入BaiduPage类,传入浏览器驱动。然后,调用get()方法访问URL,该方法由Page类提供。接下来调用BaiduPage类中定义的元素对象,即search_input和search_button,实现相应的输入和单击操作。
更多用法
想要更好地使用poium,需要了解下面的一些使用技巧。
1、支持的定位方法
poium支持8种定位方式。

from poium import Page, PageElement
 class SomePage(Page):
     elem_id = PageElement(id_="id")
     elem_name = PageElement(name="name")
     elem_class = PageElement(class_name="class")
     elem_tag = PageElement("tag"="input")
     elem_link_text = PageElement(link_text="this_is_link")
     elem_partial_link_text = PageElement(partial_link_text="is_link")
     elem_xpath = PageElement(xpath='//*[@id="kk"]')
     elem_css = PageElement(css="#id")

2、设置元素超时时间
通过timeout参数可设置元素超时时间,默认为10s

from poium import Page,PageElement
class BaiduPage(Page):
    search_input = PageElement(id_="kw", timeout=5)
    search_button = PageElement(id_="su", timeout=30)

3、设置元素描述
当一个Page类中定义的元素非常多时,必须通过注释来增加可读性,这时可以使用describe参数。

from poium import Page, PageElement


class LoginPage(Page):
    """
    登录Page类
    """
    username = PageElement(css="#loginAccount", describe="用户名")
    password = PageElement(css="#loginPwd", describe="密码")
    login_button = PageElement(css="#login_bin",describe="登录按钮")
    user_info = PageElement(css="a.nev_user_name > span",describe="用户信息")

需要强调的是,describe参数并无实际意义,只是增加了元素定义的可读性
4、定义一组元素
当我们要定位一组元素时,可以使用PageElements类。

from poium import Page, PageElement


class ResultPage(Page):
    # 定位一组元素
    search_result = PageElements(xpath="//div/h3/a")

poium极大地简化了Page层的定义,除此之外,它还提供了很多的API,如PageSelect类简化了下拉框的处理等。读者可以到GitHub项目中查看相关信息。目前,poium已经在Web自动化项目中得到了很好的应用。
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Selenium Page Object模式是一种设计模式,用于将页面的元素和操作封装到一个类中,以便于测试代码的编写和维护。通过使用Page Object模式,可以将测试代码与页面的具体实现细节分离,提高测试代码的可读性和可维护性。 在引用的代码中,可以看到使用了Page Object模式的一些示例。首先,通过声明一个包含两个元素的元组,存放操作方式及内容,来定位页面元素\[2\]。然后,通过声明一个方法,封装对元素的操作,来实现对页面元素的操作\[2\]。最后,在构造方法中传入浏览器,并和测试用例类使用同一浏览器\[2\]。 此外,还可以看到在引用的代码中,使用了selenium库的webdriver模块来启动浏览器、访问网页,并进行元素定位和操作\[1\]\[3\]。 总结来说,Selenium Page Object模式是一种将页面元素和操作封装到类中的设计模式,可以提高测试代码的可读性和可维护性。在使用Page Object模式时,可以通过声明元组来定位页面元素,通过声明方法来封装对元素的操作,并在构造方法中传入浏览器对象。 #### 引用[.reference_title] - *1* [Python+Selenium自动化测试(五):Page Object模式](https://blog.csdn.net/Max_138015/article/details/120028865)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [selenium设计模式之------PageObject,自动化测试分层设计思想](https://blog.csdn.net/weixin_42976139/article/details/102368263)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值