pageobject的重要思想:
方法意义:
1)用公共方法代表UI所提供的功能
2)方法应该返回其他的pom或者用于断言的数据
3)pageobject页面和方法中不要加断言,断言尽量放在测试用例中
4)登陆成功和失败在pageobject中写成不同的方法,相同的部分可以再次封装成方法
字段意义:
不要暴露页面内部的元素给外部,不需要建模UI内的所有元素
实战:
清理数据adb命令:
adb shell pm clear com.xueqiu.android
1、一个简单的例子:雪球->自选页->恒瑞医药的股价
1)一个简单的Po例子目录接下:driver主要解决app启动的问题;page主要解决每个页面的问题;testcase主要解决测试用例的问题
2)AndroidClient.py的代码如下:主要负责启动app
from appium import webdriver
from appium.webdriver.webdriver import WebDriver
class AndroidClient(object):
driver:WebDriver
@classmethod
def installApp(cls)->WebDriver:
caps = {}
# caps['app']='app存放的位置' #安装app
caps["platformName"] = "android"
caps["deviceName"] = "hogwarts"
caps["appPackage"] = "com.xueqiu.android"
caps["appActivity"] = ".view.WelcomeActivityAlias"
caps["autoGrantPermissions"] = "true" # 自动授权
# caps["noReset"] = True
cls.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
cls.driver.implicitly_wait(20) # 隐式等待
return cls.driver
@classmethod
def restartApp(cls)->WebDriver:
caps = {}
caps["platformName"] = "android"
caps["deviceName"] = "hogwarts"
caps["appPackage"] = "com.xueqiu.android"
caps["appActivity"] = ".view.WelcomeActivityAlias"
# caps["autoGrantPermissions"] = "true" # 启动的时候将,noReset设置成True就不需要这一句代码自动授权
caps["noReset"] = True
cls.driver = webdriver.Remote("http://localhost:4723/wd/hub", caps)
cls.driver.implicitly_wait(20) # 隐式等待
return cls.driver
3)Mainpage.py主要负责进入主页,所以需要在构造函数中调用app启动的方法,然后点击“自选”和“行情”页面等操作,这样每次调用MainPage类都会重启app
from page_object.page.SelectedPage import SelectedPage
from page_object.driver.AndroidClient import AndroidClient
class MainPage(object):
def __init__(self):
AndroidClient.restartApp()
def gotoSelected(self):
AndroidClient.driver.find_element_by_xpath("//*[@text='自选']")
AndroidClient.driver.find_element_by_xpath("//*[@text='自选']").click() #定位两次
return SelectedPage()
SelectedPage.py,SelectedPage.py这个页面主要负责获取“自选”页面上股票的价格信息:
from page_object.driver.AndroidClient import AndroidClient
class SelectedPage(object):
def addDefault(self):
return self
def getPriceByName(self,name):
#todo:
#AndroidClient.driver.find_element_by_xpath(
#"//*[contains(@resource-id,'portfolio_stockName') and @text='恒瑞医药']/../../../..//*[contains(@resource-id,'item_layout') and @instance=20]").click()
price=AndroidClient.driver.find_element_by_xpath("//*[contains(@resource-id,'portfolio_stockName') and @text='恒瑞医药']/../../../..//*[contains(@resource-id,'item_layout') and @instance=20]").text
return float(price)
4)test_selected_page.py测试用例
from page_object.page.MainPage import MainPage
class TestSelected(object):
def test_price(self):
main=MainPage()
assert main.gotoSelected().getPriceByName("恒瑞医药")==79.84
def test_add_stock(self):
pass
2、进行简单改造:
(1)app不是每次都需要启动,很多用例可以只启动一次,所以将app的启动操作单独拿出来
(2)driver,后面可能会变成微信,h5等,也需要单独将driver的初始化拿出来,将AndroidClient.driver封装成self.driver
(3)改造定位的元素的方法,让代码更简单
修改后的目录结构:
AndroidClient.py内容不变,同上
App.py:启动app,启动之后返回主页面
#App.py
from page_object.driver.AndroidClient import AndroidClient
from page_object.page.MainPage import MainPage
class App(object):
@classmethod
def main(cls):
AndroidClient.restartApp()
return MainPage() #return的是一个类不是一个Python文件
BasePage.py:将类级别的driver在init中变成self.driver
from page_object.driver.AndroidClient import AndroidClient
from appium.webdriver.webelement import WebElement
from selenium.webdriver.common.by import By
class BasePage(object):
def __init__(self):
self.driver=AndroidClient.driver
def find(self,kv)->WebElement:
return self.driver.find_element(*kv)
Mainpage.py:主页面继承BasePage类,只做主页面负责的工作,修改查找element的方法:
修改前:self.driver.find_element_by_xpath("//*[@text='自选']")
修改后:在Basepage类中定义find方法主要使用driver.find_elment方法, 在Mainpage.py中:self.find(By.XPATH,"//*[@text='自选']")
#Mainpage.py
from selenium.webdriver.common.by import By
from page_object.page.BasePage import BasePage
from page_object.page.SelectedPage import SelectedPage
from page_object.driver.AndroidClient import AndroidClient
class MainPage(BasePage):
def gotoSelected(self):
#self.driver.find_element_by_xpath("//*[@text='自选']")
#self.driver.find_element_by_xpath("//*[@text='自选']").click() #定位两次
zixuan = (By.XPATH, "//*[@text='自选']")
self.find(zixuan) # self.driver.find_element(By.XPATH,"//*[@text='自选']")
self.find(zixuan).click()
return SelectedPage()
SelectedPage.py,定位元素中,使用%s占位符
#SelectedPage.py
from selenium.webdriver.common.by import By
from appium.webdriver.common.mobileby import MobileBy #是对By的补充,只能使用在app中
from page_object.driver.AndroidClient import AndroidClient
from page_object.page.BasePage import BasePage
class SelectedPage(BasePage):
def addDefault(self):
return self
def getPriceByName(self,name):
#AndroidClient.driver.find_element_by_xpath(
#"//*[contains(@resource-id,'portfolio_stockName') and @text='恒瑞医药']/../../../..//*[contains(@resource-id,'item_layout') and @instance=20]").click()
#price=self.driver.find_element_by_xpath("//*[contains(@resource-id,'portfolio_stockName') and @text='"+name+"']/../../../"
# "..//*[contains(@resource-id,'item_layout') and @instance=20]").text
priceLocator=(By.XPATH,"//*[contains(@resource-id,'portfolio_stockName') and @text='%s']" %name+
"/../../../..//*[contains(@resource-id,'item_layout') and @instance=20]")
price=self.find(priceLocator).text
return float(price)
test_selected_page.py,直接使用APP.main()方法
#test_selected_page.py
from page_object.page.app import App
class TestSelected(object):
def test_price(self):
assert App.main().gotoSelected().getPriceByName("恒瑞医药")==78.88
def test_add_stock(self):
pass