PO设计模式在Appium中的应用(1)——框架初步设计

1.PageObject设计模式

传统测试用例的问题

1)无法适应UI变化,UI变化会导致大量的case需要修改

2)大量的样板代码driver find click

3)无法清晰的表达业务用例场景

PageObject模式原则

方法意义

  • 用公用方法代表UI所提供的服务
  • 方法应该返回其他的PageObject或者返回用于断言的数据
  • 同样的行为不同的结果可以建模为不同的方法
  • 不要在方法内加断言

字段意义

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

PO模式封装的主要组成元素

  • Page对象:完成对页面的封装
  • Driver对象:完成对web、android、ios、接口的驱动
  • 测试用例:调用Page对象实现业务并断言
  • 数据封装:配置文件和数据驱动
  • Utils:其他功能封装,改进原生框架的不足

 2.测试框架的改进

po是一个设计思想,我们可以先搭架子,再去补充细节,以企业微信app实战为例

1)首先创建一个page包,然后创建basepage模块,创建基类

basepage.py

from appium.webdriver.webdriver import WebDriver
class BasePage():
#WebDriver默认为None,这个一定要注意⚠️加上,因为如果不加默认None,后面跳转下一个页面的时候会传递一个
self.driver,然后继承BasePage又会传递一个driver,造成重复
def __init__(self, driver: WebDriver = None): self._driver = driver

2)然后创建一个app模块,app模块中的App类继承BasePage

from testenter.page.basepage import BasePage
from testenter.page.main import Main
class App(BasePage):
    #启动app
    def start(self):
        return self
    #停止app
    def stop(self):
        pass
    #重启app
    def restart(self):
        pass
  #跳转到主页面,传递self._driver def main(self): return Main(self._driver)

其中有两个问题:1.start方法为什么要加上返回值return self❓因为如果不加上返回自己,则无法调用到main方法

2. start方法不可以放在basepage里么❓可以,但是我们是为了进行框架的设计,  这样我们如果要做web的话,可以放一起,我们可以再创建web的模块,专门用来存放web的一些基类的操作

3. 跳转的时候传了driver,继承basepage也继承了driver,会不会重复呀❓不会,因为继承的basepage中的driver是None

3)根据po设计原则,跳转到主页面,我们再创建一个main页面,即main模块

from testenter.page.addresslistpage import AddresslistPage
from testenter.page.basepage import BasePage
class Main(BasePage):
#跳转到消息tab页 def goto_message(self): pass   #跳转到通讯录列表页 def goto_addresslist(self):
     #返回通讯录列表页 return AddresslistPage(self._driver)   #跳转到工作台页面 def goto_workbench(self): pass   #跳转到我的页面 def goto_myprofile(self): pass

如果我们不知道怎么给页面命名的时候,我们可以通过获取当前页面,为页面命名获取到的页面的名字,如下:adb shell dumpsys window | grep mCurrent

4)创建通讯录列表页addresslistpage模块,点击添加成员,返回邀请成员页面,因此创建 click_addmember(添加成员)方法

from testenter.page.basepage import BasePage
# from testenter.page.memberinvitepage import MemberInvitePage
class AddresslistPage(BasePage):
    def click_addmember(self):
        from testenter.page.memberinvitepage import MemberInvitePage
        return MemberInvitePage(self._driver)

5)创建memberinvitepage.py,点击手动输入添加,即click_memualadd方法,

跳转到添加联系人页面

# from testenter.page.addresslistpage import AddresslistPage
from testenter.page.basepage import BasePage
# from testenter.page.contactaddpage import ContactAddPage
class MemberInvitePage(BasePage):
    def click_memualadd(self):
        from testenter.page.contactaddpage import ContactAddPage
        return ContactAddPage(self._driver)

    def click_back(self):
        from testenter.page.addresslistpage import AddresslistPage
        return AddresslistPage(self._driver)

6)创建contactaddpage.py(添加联系人页面)手动输入姓名,手机号,性别,点击保存,返回至添加成员页面,即memberinvitepage.py

# from testenter.page.memberinvitepage import MemberInvitePage
class ContactAddPage(BasePage):
    def input_name(self):
        # 没有完成跳转,return self就可以了
        return self

    def set_gender(self):
        return self

    def input_phone(self):
        return self

    def click_save(self):
        from testenter.page.memberinvitepage import MemberInvitePage
        return MemberInvitePage(self._driver)

7)页面布局已经设计好,开始设计用例,如下创建一个test_case的包来存放测试用例,创建test_contact.py文件创建添加联系人的测试用例

from testenter.page.app import App
class TestAddContact():
    # 注意⚠️这里一开始App调用的时候没有加括号报错了,没有加括号相当于调用了App类,但是没有初始化对象,
#只有对象才能调用方法 # 另外start方法要加上返回值return self,否则无法调用到main方法 def setup(self): self.main = App().start().main() def test_addtact(self): self.main.goto_addresslist().click_addmember().click_memualadd(). \ input_name().input_phone(). \ set_gender().click_save().click_back() 

⚠️⚠️❗️这里有一个细节,就是为什么有的导入模块的时候不在模块最上方导入,而是在方法中局部导入呢,这是因为当我一开始在模块上方导入的时候报错了,报错信息如下:

最下方报错如下:错误一般从后向前看,这是循环导入的问题

 

什么是循环导入呢,就是你在这个页里面导入了下一页,最后又返回了当前页面,python不支持循环导入

因为是由python的机制决定的,当你在导入一个包的时候会对这个包进行初始化分析,编译;当它导入包的时候,会初始化导入包中又导入的包,然后发现又导入的包中有自己的包,就还没有初始化完当前包,又重新初始化,如下图所示:

解决方案:完成局部导入,就是你用的时候再导入,就不在上面导入了

  

猜你喜欢

转载自www.cnblogs.com/zhaikunkun/p/12730750.html