pageobject的重要思想:

方法意义:

1)用公共方法代表UI所提供的功能

2)方法应该返回其他的pom或者用于断言的数据

3)pageobject页面和方法中不要加断言,断言尽量放在测试用例中

4)登陆成功和失败在pageobject中写成不同的方法,相同的部分可以再次封装成方法

字段意义:

不要暴露页面内部的元素给外部,不需要建模UI内的所有元素

实战:

清理数据adb命令:

adb shell pm clear com.xueqiu.android

1、一个简单的例子:雪球->自选页->恒瑞医药的股价

pytest request 日志打印 pytest pageobject_测试用例

1)一个简单的Po例子目录接下:driver主要解决app启动的问题;page主要解决每个页面的问题;testcase主要解决测试用例的问题

pytest request 日志打印 pytest pageobject_测试用例_02

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)改造定位的元素的方法,让代码更简单

修改后的目录结构:

 

pytest request 日志打印 pytest pageobject_App_03

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