Page Object设计模式
下面我们将通过例子介绍这种设计模式的使用。
def test_baidu_search_case1(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_case2(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_case3(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 层的元素定位,而不需要关心在哪些测试用例当
中使用了这些元素。在编写测试用例时,也不需要关心元素是如何定位的。
1.1PO设计模式
PO设计模式为每个待测页面创建一个对象,将业务逻辑代码和测试代码分离,业务逻辑代码被封装在页面类中,下面我们把对百度页面的操作封装在页面类中,我们的测试代码只需要调用页面类的方法即可。
page.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) # class 定位 def by_class(self, class_name): return self.driver.find_element_by_class_name(class_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.execute_script(script)
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()
test_page_page.py
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)
1.2poium 测试库
poium 是一个基于Selenium/appium 的Page Object 测试库,最大的特点是简化了Page层元素的定义。
项目地址:https://github.com/defnngj/poium。
支持 pip 安装。
> pip install poium
1.2.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.send_keys = "selenium" page.search_button.click() …
1.2.2更多用法
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_btn', describe=" 登 录 按 钮 ") user_info = PageElement(css="a.nav_user_name > span", describe="用户信息")