文章目录
简介
PO模式(Page Object Model)是自动化测试项目开发实践的最佳设计模式之一。
它的主要用途是把一个具体的页面转换成编程语言当中的一个对象,页面特性转化成对象属性,页面操作转换成对象方法。
在自动化测试当中,主要用来实现对页面操作和测试,逻辑的一个分离,PO思想最开始来源于马丁富勒(marktin Flewer)在2004年发表的一篇文章。最初是叫作Window driver,后来selenium沿用这种思想,后来就改成了POM。
PO模式的核心思想是通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化,只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性。
优点
- 代码可读性高,减少冗余代码;
- 增加用例的可维护性,业务代码和测试代码被分开,降低耦合性维护成本低;
- 增强复用性。
基本原则
方法意义
- 用公共方法来代表UI所提供的功能
如:首页的搜索功能,名站功能 - 方法应该返回其他的page object或者返回用于断言的数据
如,首页点击搜索框进入搜索页面 - 相同的行为不同的结果,可以建模不同的方法
如:登录行为可以拆分成登录成功(进入首页),或者登录失败(仍然停留在登录页面) - 不会在PO内创建断言
字段意义
- 不要暴露页面内的元素给外部
如:页面内的按钮的id不能被其他的case调用(变化的元素都控制在PO内部) - 不需要建模整个页面元素
如:每个页面的元素不需要全部列出来,用到什么写什么
用例分层
基于 POM 的用例组织结构,良好的分层机制,让不同层去做不同类型的事情,让代码结构清晰,增加复用性。
一般而言,可以分以下几层:
- page:完成对页面的封装
- testcase:调用各类 page 完成业务流程并进行断言
- driver:完成对 Web、Android、iOS、接口的驱动
- data:配置文件和数据驱动
- utils:其他便捷的功能封装,可选
demo
测试demo:雪球app
操作步骤:打开首页 --> 点击搜索框 --> 输入股票代码 --> 断言股票价格是否大于1快
原始测试脚本
from appium import webdriver
class TestDemo:
def setup(self):
caps = {
'platformName': 'Android',
'appPackage': 'com.xueqiu.android',
'appActivity': '.view.WelcomeActivityAlias',
'noReset': True,
'skipServerInstallation': True
}
self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', caps)
self.driver.implicitly_wait(10)
def test_search(self):
"""
原脚本
"""
self.driver.find_element_by_id("com.xueqiu.android:id/home_search").click() # 点击首页搜索框
self.driver.find_element_by_id("search_input_text").send_keys('二三四五') # 输入股票代码
self.driver.find_element_by_id("name").click() # 点输入联想列表的股票名称
price = self.driver.find_element_by_id("current_price") # 获取股票价格
assert float(price.text) > 1.0 # 断言是否大于1元
def teardown(self):
self.driver.quit()
PO模式
编写用例的顺序
- 根据界面封装page类与方法()
- 编写用例,不断重构明确 page 里方法的入参和返回值
- 开始实现 page 内的方法
- 把这个页面需要的一些核心操作封装成方法
- 在有页面发生跳转的情况下,发生跳转的方法需要返回跳转页面的类对象
testcase
from page.app import App
class TestDemo:
def setup(self):
self.search_page = App.start().to_search()
def test_search_po(self):
self.search_page.search('二三四五')
assert self.search_page.get_current_price() > 1.0
def teardown(self):
App.quit()
page
- app.py
from appium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver
from page.main_page import MainPage
class App:
driver: WebDriver = None
@classmethod
def start(cls):
"""
启动app:后进入首页
:return: 首页
"""
caps = {
'platformName': 'Android',
'appPackage': 'com.xueqiu.android',
'appActivity': '.view.WelcomeActivityAlias',
'noReset': True,
'skipServerInstallation': True
}
cls.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', caps)
cls.driver.implicitly_wait(10)
return MainPage(cls.driver)
@classmethod
def quit(cls):
cls.driver.quit()
- main_page.py
from selenium.webdriver.remote.webdriver import WebDriver
from page.search_page import SearchPage
class MainPage(object):
def __init__(self, driver: WebDriver):
self.driver = driver
def to_search(self):
self.driver.find_element_by_id("com.xueqiu.android:id/home_search").click() # 点击首页搜索框
return SearchPage(self.driver)
- search_page.py
from selenium.webdriver.remote.webdriver import WebDriver
class SearchPage(object):
def __init__(self, driver: WebDriver):
self.driver = driver
def search(self, keyword):
self.driver.find_element_by_id("search_input_text").send_keys(keyword) # 输入股票代码
self.driver.find_element_by_id("name").click() # 点输入联想列表的股票名称
return self
def get_current_price(self):
return float(self.driver.find_element_by_id("current_price").text)