【unittest单元测试框架】(9)认识Page Object


认识Page Object  

  Page Object 是 UI 自动化测试项目开发实践的最佳设计模式之一,它的主要特点体现在对界面交互细节的封装上,使测试用例更专注于业务的操作,从而提高测试用例的可维护性。   当为 Web 页面编写测试时,需要操作该 Web 页面上的元素。然而,如果在测试代码中直接操作 Web 页面上的元素,那么这样的代码是极其脆弱的,因为 UI 会经常变动。   page 对象的一个基本经验法则是:凡是人能做的事,page 对象通过软件客户端都能做到。因此,它应当提供一个易于编程的接口,并隐藏窗口中底层的部件。当访问一个文本框时,应该通过一个访问方法(Accessor Method)实现字符串的获取与返回,复选框应当使用布尔值,按钮应当被表示为行为导向的方法名。page 对象应当把在 GUI 控件上所有查询和操作数据的行为封装为方法。   一个好的经验法则是,即使改变具体的元素,page 对象的接口也不应当发生变化。   尽管该术语是 page 对象,但并不意味着需要针对每个页面建立一个这样的对象。例如,页面上有重要意义的元素可以独立为一个 page 对象。经验法则的目的是通过给页面建模,使其对应用程序的使用者变得有意义。   Page Object 是一种设计模式,在自动化测试开发中应遵循这种设计模式来编写代码。   Page Object 应该遵循以下原则进行开发: 
  • Page Object 应该易于使用。
  • 有清晰的结构,如 PageObjects 对应页面对象,PageModules 对应页面内容。
  • 只写测试内容,不写基础内容。
  • 在可能的情况下防止样板代码。
  • 不需要自己管理浏览器。
  • 在运行时选择浏览器,而不是类级别。
  • 不需要直接接触 Selenium。 
  创建 base.py 文件,内容如下:
# -*- coding:utf-8 -*-
# filename: base.py
# author: hello.yin
# date: 2021/11/17 15:50

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_)

    # xpath定位
    def by_xpath(self, xpath):
        return self.driver.find_element_by_xpath(xpath)

    # class定位
    def by_class(self, class_name):
        return self.driver.find_element_by_class_name(class_name)

    # name定位
    def by_name(self, name):
        return self.driver.find_element_by_name(name)

    # 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
    def get_text(self, xpath):
        return self.by_xpath(xpath).text

    # 执行js脚本
    def js(self, script):
        self.driver.execute_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 文件。  
# -*- coding:utf-8 -*-
# filename: baidu_page.py
# author: hello.yin
# date: 2021/11/17 16:12

from base import BasePage


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

    def search_input(self, search_key):
        self.by_id("kw").clear()
        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_button()方法中使用了父类的 self.by_id()方法来定位元素,比原生的 Selenium 方法简短了不少。   在测试用例中,使用 BaiduPage 类及它所继承的父类中的方法。   因为前面封装了元素的定位,所以在编写测试用例时会方便不少,当需要用到哪个 Page类时,只需将它传入浏览器驱动,就可以使用该类中提供的方法了。
# -*- coding:utf-8 -*-
# filename: test_BaiduPage.py
# author: hello.yin
# date: 2021/11/17/ 16:23

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.Firefox()

    def test_baidu_search(self):
        page = BaiduPage(self.driver)
        page.open()
        page.search_input("selenium")
        page.search_button()
        sleep(3)
        self.assertEqual(page.get_title(), "selenium_百度搜索")

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


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

 执行结果:

Testing started at 16:32 ...
C:\Users\yzp\AppData\Local\Programs\Python\Python37\python.exe "C:\Program Files\JetBrains\PyCharm 2018.2\helpers\pycharm\_jb_unittest_runner.py" --path D:/00test/base_practice/page_object/page/test_BaiduPage.py
Launching unittests with arguments python -m unittest D:/00test/base_practice/page_object/page/test_BaiduPage.py in D:\00test\base_practice\page_object\page


Ran 1 test in 11.866s

OK

Process finished with exit code 0