zoukankan      html  css  js  c++  java
  • 自动化测试必会—PageObject 设计模式

    PageObject 是什么?

    对页面对象进行抽象处理,(页面对象包含:页面元素、button点击、文本框输入、选项框选择等等)。使代码能在页面元素发生改变后,尽量减少测试脚本的改动量,最大程度支持代码的可重复性使用,同时使得测试框架结构合理、清晰、代码更加模块化,避免冗余、藕合性过高。

    PageObject 有哪些特征?

    • 页面封装成Page 类,页面元素为Page 类的成员元素,页面功能的实现放在Page 类的方法里。

    • 将一个待测页面(或者待测试对象)封装成一个类(Class),例如:登录页面,把它称作Page 类。Page 类里包括了这个页面(或者待测试对象)上的所有的元素(登录页面的url链接,username/password文本框的输入,图片验证码的获取,登录/注册button的点击),以及针对页面元素的操作方法(单步操作或者多步操作,一般会定义类方法)。

    • 注意:这个登录页面的Page 类里仅仅包括登录页面,一般不包括针对(除去登录页面)其他页面的操作。

    • 针对这个Page 类定义一个测试类,在测试类调用Page 类的各个类方法完成自动化测试。也就是测试代码和被测试页面的页面代码解耦,当页面本身发生变化,例如:元素定位发生改变、页面布局改变后,仅需要更改相对应的Page 类的代码,而无须更改测试类的代码。PageObject 模式减少了代码冗余,可以使业务流程变得清晰易读,降低了测试代码维护成本。

     

    如何实现PageObject 设计模式?

    如下图所示,是PageObject 的经典设计模块流程图:
    图片

    从图中可知,在测试类里,我们会定义许多测试方法,这些测试方法里,会包含对页面对象实例的调用;而页面对象实例,是通过页面对象类进行初始化操作生成的;对于许多页面对象类都存在的通用操作,我们会提取到页面对象基类里。

    通过这种方法,可以实现:

    • 一个页面元素在整个项目中,仅存在一处定义,其他都是调用(如果页面元素发生变更,只修改此处即可);

    • Page 类通用的操作进一步提取到BasePage 类,减少了代码冗余。

    PageObject 的Python 库

    在Python 里,有专门针对PageObject 的Python 库Page Objects。使用Page Objects 可以迅速实现PageObject 模式。

    安装 page_objects
    Python 常用安装命令:pip install page_objects

    PageObject 登录页面实战示例

    #从page_objects包中引入PageObject和PageElement模块
    from page_objects import PageObject, PageElement
     
    from selenium import webdriver
     
    class LoginPage(PageObject):
     
            username = PageElement(id_='username')
     
            password = PageElement(name='password')
     
            login = PageElement(css='input[type="submit"]')
     
    driver = webdriver.PhantomJS()
     
    driver.get("http://www.baidu.com")
     
    #实例化一个类对象为:page
    page = LoginPage(driver)
     
    #给类对象的username属性赋值为:13057891256(相当于手工在登录页面的用户名输入框输入:13057891256)
    page.username = '13057891256'
     
    #给类对象的password属性赋值为:test_123(相当于手工在登录页面的密码输入框输入:test_123)
    page.password = 'test_123'
     
    #断言登录页面输入的用户名是否是:13057891256
    assert page.username.text == '13057891256'
     
    #让类对象点击登录页面的登录按钮(相当于手工在登录页面点击登录按钮)
    page.login.click()
     

    以上只是拿登录页面做了一个PageObject 的简单示例,PO(PageObject) 模式的知识可远不止这些,接下来请看PageObject 设计模式在项目实战中的最佳实践。

    项目实战——PageObject 设计模式的最佳实践

    以下目录结构是在使用PageObject 设计模式后的测试框架结构:

    |--APITestPO
     
        |--pages
     
            |--ones.py
     
            |--base_page.py
     
        |--tests
     
            |--test_ones.py
     
            |--__init__.py
     
        |--common
     
            |--__init__.py
     
            |--selenium_action.py
     
            |--requests_action.py

    __init__.py 是空文件。

    ones.py 文件只包括Page 本身的元素、对象和操作,而不包括其他的部分,比如对浏览器 Driver 的初始化、对 requests.Session() 的初始化、登录等操作。

    pages/ones.py 文件内容如下:

    from selenium.webdriver.common.by import By
     
    from selenium.webdriver.support.ui import WebDriverWait
     
    from selenium.webdriver.support import expected_conditions as EC
     
    from page_objects import PageObject, PageElement
     
    from pages.base_page import BasePage
     
    class OneAI(BasePage):
     
        PROJECT_NAME_LOCATOR = '[class="company-title-text"]'
     
        NEW_PROJECT_LOCATOR = '.ones-btn.ones-btn-primary'
     
        new_project = PageElement(css=NEW_PROJECT_LOCATOR)
     
        def __init__(self, login_credential, target_page):
     
            super().__init__(login_credential, target_page)
     
        def get_project_name(self):
     
            try:
     
                project_name = WebDriverWait(self.driver, 30).until(
     
                    EC.presence_of_element_located((By.CSS_SELECTOR, self.PROJECT_NAME_LOCATOR)))
     
                return project_name.get_attribute("innerHTML")
     
            except TimeoutError:
     
                raise TimeoutError('Run time out')

    tests/test_ones.py 文件内容如下:

    import pytest
     
    from pages.ones import OneAI
     
    class TestOneAI:
     
        # 注意:email和密码需要更改成你自己的账户密码
     
        @pytest.mark.parametrize('login_data, project_name, target_page', [({"password": "wuliangceshizhidaoIsGood", "email": "pleasefollowwuliangceshizhidao@outlook.com"}, {"project_name":"GOODTEST"}, {"target_page": "https://ones.ai/project/#/home/project"})])
     
        def test_project_name_txt(self, login_data, project_name, target_page):
     
            print(login_data)
     
            one_page = OneAI(login_data, target_page)
     
            actual_project_name = one_page.get_project_name()
     
            assert actual_project_name == project_name["project_name"]

    可以看到,test_ones.py 这个测试类就变得非常简洁。它只包括一个测试方法,即 test_project_name_txt。这个函数用来测试我们拿到的 project name 而不是等于我们提供的那个值,即GOODTEST。

    需要注意,在测试类中,应该仅仅包括对 Page 类的各种方法的调用,而不能在测试类中直接去操作测试类对象生成新的功能。

    将关于Selenium/WebDriver 和 Requests 的操作分别拆分到selenium_action.py 和 requests_action.py 里。

    selenium_action.py 文件内容如下:

    from selenium import webdriver
     
    class SeleniumAction(object):
     
        @staticmethod
     
        def initial_driver(browser_name='chrome'):
     
            browser_name = browser_name.lower()
     
            if browser_name not in {'chrome', 'firefox', 'ff', 'ie'}:
     
                browser_name = 'chrome'
     
            if browser_name == 'chrome':
     
                browser = webdriver.Chrome()
     
            elif browser_name in ('firefox', 'ff'):
     
                browser = webdriver.Firefox()
     
            elif browser_name == 'ie':
     
                webdriver.Ie()
     
            browser.maximize_window()
     
            browser.implicitly_wait(60)
     
            return browser
     
        @staticmethod
     
        def cookie_to_selenium_format(cookie):
     
            cookie_selenium_mapping = {'path': '', 'secure': '', 'name': '', 'value': '', 'expires': ''}
     
            cookie_dict = {}
     
            if getattr(cookie, 'domain_initial_dot'):
     
                cookie_dict['domain'] = '.' + getattr(cookie, 'domain')
     
            else:
     
                cookie_dict['domain'] = getattr(cookie, 'domain')
     
            for k in list(cookie_selenium_mapping.keys()):
     
                key = k
     
                value = getattr(cookie, k)
     
                cookie_dict[key] = value
     
            return cookie_dict

    selenium_action.py 里包括了所有针对Selenium 的操作,以后针对浏览器的各种操作全部都放在这个py 文件里。

    requests_action.py 文件内容如下:

    import json
     
    import traceback
     
    import requests
     
    from requests.packages.urllib3.exceptions import InsecureRequestWarning
     
    # Disable https security warning
     
    requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
     
    class SharedAPI(object):
     
        def __init__(self):
     
            self.s = requests.session()
     
            self.login_url = 'https://ones.ai/project/api/project/auth/login'
     
            self.header = {
     
                "user-agent": "user-agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36",
     
                "content-type": "application/json"}
     
        def login(self, login_credential):
     
            try:
     
                result = self.s.post(self.login_url, data=json.dumps(login_credential), headers=self.header, verify=False)
     
                if int(result.status_code) == 200:
     
                    pass
     
                else:
     
                    raise Exception('login failed')
     
                return result
     
            except RuntimeError:
     
                traceback.print_exc()
     
        def post_api(self, url, **kwargs):
     
            return self.s.post(url, **kwargs)
     
        def get_api(self, url, **kwargs):
     
            return self.s.get(url, **kwargs)

    requests_action.py 里包括所有对requests 这个库的操作。

    base_page.py 文件内容如下:

    import json
     
    import traceback
     
    import requests
     
    from selenium import webdriver
     
    from page_objects import PageObject, PageElement
     
    from common.requests_action import SharedAPI
     
    from common.selenium_action import SeleniumAction
     
    class BasePage(PageObject):
     
        def __init__(self, login_credential, target_page):
     
            self.api_driver = SharedAPI()
     
            self.loginResult = self.api_driver.login(login_credential)
     
            self.driver = SeleniumAction.initial_driver()
     
            self._api_login(login_credential, target_page)
     
        def _api_login(self, login_credential, target_page):
     
            target_url = json.loads(json.dumps(target_page))
     
            assert json.loads(self.loginResult.text)["user"]["email"].lower() == login_credential["email"]
     
            all_cookies = self.loginResult.cookies._cookies[".ones.ai"]["/"]
     
            self.driver.get(target_url["target_page"])
     
            self.driver.delete_all_cookies()
     
            for k, v in all_cookies.items():
     
                    self.driver.add_cookie(SeleniumAction.cookie_to_selenium_format(v))
     
            self.driver.get(target_url["target_page"])
     
            return self.driver

    可以看到,在base_page.py 里,我们初始化 requests.Session() 和浏览器的 Driver 的方式是通过调用 SharedAPI 和 SeleniumAction 这两个类。然后 BasePage 这个类里现在只包括各个 Page 类可以共用的函数,而不再包括无关的操作。

    小结

    今天的分享主要和大家初步介绍了PageObject 设计模式,PageObject 模式是自动化测试里的一个经典设计模式。通过 PageObject 我们可以实现元素定位、元素、元素操作的分离,从而让自己的自动化测试框架更加具备可重用性、且可低成本维护。

    不知你是否发现,通过PageObject 设计模式,可以把自动化测试框架进行了框架代码和业务代码的剥离。此后我们的框架可以看起来结构清晰,易于理解,降低其他同事的学习成本。

    欢迎关注【无量测试之道】公众号,回复【领取资源】
    Python编程学习资源干货、
    Python+Appium框架APP的UI自动化、
    Python+Selenium框架Web的UI自动化、
    Python+Unittest框架API自动化、

    资源和代码 免费送啦~
    文章下方有公众号二维码,可直接微信扫一扫关注即可。

    备注:我的个人公众号已正式开通,致力于测试技术的分享,包含:大数据测试、功能测试,测试开发,API接口自动化、测试运维、UI自动化测试等,微信搜索公众号:“无量测试之道”,或扫描下方二维码:

     添加关注,让我们一起共同成长!

  • 相关阅读:
    【NOIP2016提高A组8.12】奇袭
    【NOIP2016提高A组8.12】礼物
    tarjan算法
    【NOIP2016提高A组8.12】总结
    【NOIP2016提高A组8.11】自然数
    【NOIP2016提高A组8.12】通讯
    【NOIP2016提高A组8.11】种树
    【NOIP2016提高A组8.11】钱仓
    【NOIP2016提高组A组7.16】大鱼海棠
    【NOIP2012模拟8.20】Memory
  • 原文地址:https://www.cnblogs.com/Wu13241454771/p/14349808.html
Copyright © 2011-2022 走看看