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不支持循环导入
解决方案:完成局部导入,就是你用的时候再导入,就不在上面导入了