摘要
- PageObject设计模式
- PageObject六大设计原则
- PageObject第一个例子
- BasePage封装改进
- PO自动化测试实战内容
1、PageObject的历史及发展
1、https://martinfowler.com/bliki/PageObject.html
2、https://github.com/SeleiumHQ/selenium/wiki/PageObjects
3、https://selenium-python.readthedocs.io/page-objects.html
4、https://pypom.readthedocs.io/en/latest/
2、Page Object Model的基本原则
2.1 方法
- 用公共方法代表UI所提供的功能
- 方法应该返回其他的Page Object 或者 返回哦那个与断言的数据
- 同样的行为不同的结果可以建模不同的方法
- 不要在方法内加断言
2.2 字段
- 不要暴露页面内容的元素给外部
- 不需要建模UI内的所有元素
3、第一个例子
page目录下的 search_page.py:
class SearchPage():
def __init__(self, driver):
self.driver = driver
def search(self, key):
self.driver.find_element(key).click()
return self
def get_current_price(self):
return float(self.driver.find_element().text)
测试用例py文件:
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from page.search_page import SearchPage
class TestDemo:
def setup(self):
caps = {
"platformName": "android",
"deviceName": "008640dd0804",
"automationName": "uiautomator2",
"appPackage": "com.xueqiu.android",
"appActivity": ".view.WelcomeActivityAlias",
"autoGrantPermissions": "true"
}
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.find_element(AppiumBy.ID, "com.xueqiu.android:id/tv_agree").click()
self.driver.implicitly_wait(20)
def test_search_page(self):
search_page = SearchPage(self.driver)
search_page.search("alibaba")
assert search_page.get_current_price() > 10
def teardown(self):
self.driver.quit()
4、进一步封装basepage.py
basepage.py:
- 实现通用的page方法,对常用的自动化行为做封装
1、异常弹框处理:广告、好评、升级、tips等弹框
2、通用自动化能力封装
- 管理各种driver
- 减少每个page对Appium、Selenium等库的太多依赖
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
class BasePage:
def __init__(self, driver: WebDriver):
self.driver = driver
def click(self, element):
self.__wait(element)
self.driver.find_element(AppiumBy.XPATH, element).click()
def type(self, element, sendvalue):
self.__wait(element)
self.driver.find_element(AppiumBy.XPATH, element).send_keys(sendvalue)
def __wait(self, element):
WebDriverWait(self.driver, 20).until(lambda x: self.driver.find_element(AppiumBy.XPATH, element))
业务层封装:
from page.basepage import BasePage
class SearchPage(BasePage):
def search(self, key):
self.click("某元素的值")
return self.type("element", "sendvalue")
def get_current_price(self):
return float(self.driver.find_element().text)
用例层封装:
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from page.search_page import SearchPage
class TestDemo:
def setup(self):
caps = {
"platformName": "android",
"deviceName": "008640dd0804",
"automationName": "uiautomator2",
"appPackage": "com.xueqiu.android",
"appActivity": ".view.WelcomeActivityAlias",
"autoGrantPermissions": "true"
}
self.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
self.driver.find_element(AppiumBy.ID, "com.xueqiu.android:id/tv_agree").click()
self.driver.implicitly_wait(20)
def test_search_page(self):
search_page = SearchPage()
search_page.search("alibaba")
assert search_page.get_current_price() > 10
def teardown(self):
self.driver.quit()
5、完整版本的框架例子
5.1、测试用例管理:
- 使用package管理业务模块
- 使用class管理业务对象、使用method完成业务具体行为
- 数据驱动:测试数据、测试步骤、测试断言
- 测试用例:使用testcase完成测试步骤的定位,使用assertion完成业务正确性校验
- 持续集成:使用jenkins完成持续集成
5.2、基于POM的用例组织结构
- page:完成对页面的封装
- driver:完成对web、android、ios、接口的驱动
- testcase:调用各类page完成业务流程并进行断言
- data:配置文件和数据驱动
- until:其他便捷的功能封装,可选
5.3、代码示例
app.py
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver
from page.main_page import MainPage
class App:
driver: WebDriver = None
@classmethod # 类方法,不需要实例化就可以被调用,比如:App.start()
def start(cls):
caps = {
"platformName": "android",
"deviceName": "008640dd0804",
"automationName": "uiautomator2",
"appPackage": "com.xueqiu.android",
"appActivity": ".view.WelcomeActivityAlias",
"autoGrantPermissions": "true"
}
cls.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
cls.driver.find_element(AppiumBy.ID, "com.xueqiu.android:id/tv_agree").click()
cls.driver.implicitly_wait(20)
return MainPage(cls.driver)
@classmethod
def quit(cls):
cls.driver.quit()
basepage.py
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
class BasePage:
_black_list = ["image_cancel", "tips"]
def __init__(self, driver: WebDriver):
self.driver = driver
def click(self, element):
self.__handle_exception()
self.__wait(element)
self.driver.find_element(AppiumBy.XPATH, element).click()
def type(self, element, sendvalue):
self.__wait(element)
self.driver.find_element(AppiumBy.XPATH, element).send_keys(sendvalue)
def __wait(self, element):
WebDriverWait(self.driver, 20).until(lambda x: self.driver.find_element(AppiumBy.XPATH, element))
# 自动处理广告、好评、升级、tips弹框等
def __handle_exception(self):
self.driver.implicitly_wait(0)
for locator in self._black_list:
elements = self.driver.find_elements(AppiumBy.XPATH, locator)
if elements[0]:
elements[0].click()
else:
print(f"没有找到{locator}")
# page_source = self.driver.page_source
# if "image_cancel" in page_source:
# self.driver.find_element(AppiumBy.XPATH, locator).click()
# elif "tips" in page_source:
# pass
self.driver.implicitly_wait(6)
main_page.py
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver
from page.basepage import BasePage
from page.search_page import SearchPage
# 基础封装
class MainPage:
def __init__(self, driver: WebDriver):
self.driver = driver
def search_page(self):
self.driver.find_element(AppiumBy.XPATH, "xxx").click()
return SearchPage(self.driver)
# 继承了basepage的封装
class MainPage1(BasePage):
def search_page(self):
self.driver.find_element(AppiumBy.XPATH, "xxx").click()
return SearchPage(self.driver)
def search_page2(self):
self.click("xxx")
self.driver.find_element(AppiumBy.XPATH, "xxx").click()
search_page.py
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver
from page.basepage import BasePage
class SearchPage:
def __init__(self, driver: WebDriver):
self.driver = driver
def search(self, key):
self.driver.find_element(AppiumBy.XPATH, key).click()
def get_current_price(self):
return float(self.driver.find_element().text)
# 继承了basepage的封装
class SearchPage1(BasePage):
def search(self, key):
self.click("xxx")
self.driver.find_element(AppiumBy.XPATH, key).click()
def get_current_price(self):
return float(self.driver.find_element("xxx").text)
test.py
from page.app import App
import time
class TestDemo:
def setup(self):
self.search_page = App.start().search_page()
def test_search_page(self):
self.search_page.search("alibaba")
assert self.search_page.get_current_price() > 10
def teardown(self):
time.sleep(10)
App.quit()