Appium Python-Appium 设计模式问题求教

点点寒彬 · 2016年04月27日 · 最后由 zhangjg 回复于 2017年10月23日 · 1006 次阅读

背景

最近在研究Python——Appium的自动化实现,大概两个多月吧,感觉现在遇到了一些瓶颈,想请社区的牛人给一个方向,主要还是关于设计模式的。

现在的进度

正常的线性代码完成Android原生和混合App是没问题了,在写的过程中我发现实用性并不大,开发一点很小的变动我的代码就要做大改。感觉很麻烦,于是找了一个叫Page Object的设计模式,按照这个模式写,我现在实现了这么些东西。源代码在这里WeStockTest,还没写完。

元素

因为就我一个人,我就直接使用配置文件处理,代码如下:

CONNECT = {
'platformName': 'Android',
'platformVersion': '4.4.4',
'deviceName': '5136b01e',
'appPackage': 'com.weizq',
'appActivity': 'com.zztzt.android.simple.app.MainActivity',
"baseUrl": "http://127.0.0.1:4723/wd/hub"
}

COMMON = {
'native_caixun': (By.NAME, u'财讯'),
'native_hangqing': (By.NAME, u'行情'),
'native_faxian': (By.NAME, u'发现'),
'native_wo': (By.NAME, u'我'),
'data_url': '/Users/SvenWeng/PycharmProjects/WeStock/Data/data.json',
'view_title': (By.CLASS_NAME, 'android.widget.TextView')
}

公共方法

class WebDdriver(object):
def __init__(self, driver):
self.driver = driver

def __str__(self):
return 'webDdriver'

def find_element(self, *loc):
"""
定位元素,定位正确后返回元素的信息,外部调用传入元组参数必须有*,
例如:
find_element(*self.native_caixun)

:param loc: 元组类型,结构必须是(By.NAME, u'财讯')
:return: element
"""

try:
element = WebDriverWait(self.driver, 10).until(lambda x: x.find_element(*loc))
return element
except NoSuchElementException, e:
print 'Error details :%s' % (e.args[0])

def find_elements(self, *loc):
"""
定位元素,定位正确后返回元素的信息,外部调用传入元组参数必须有*,
例如:
find_elements(*self.native_caixun)

:param loc: 元组类型,结构必须是(By.NAME, u'财讯')
:return: elements
"""

try:
# return self.driver.find_elements(*loc)
elements = WebDriverWait(self.driver, 10).until(lambda x: x.find_elements(*loc))
return elements
except NoSuchElementException, e:
print 'Error details :%s' % (e.args[0])

单个页面的方法

class Caixun(AppUI):

# ----------------------财讯所有数据--------------------------
native_caixun = config.COMMON['native_caixun']

tuijian = config.CAIXUN['tuijian']
gupiao = config.CAIXUN['gupiao']
jijin = config.CAIXUN['jijin']
zhaiquan = config.CAIXUN['zhaiquan']
xinsanban = config.CAIXUN['xinsanban']
neirong = config.CAIXUN['zixunneirong']
plusbtn = config.CAIXUN['jiahao']
pindao = config.CAIXUN['pindao']

img_url = config.CAIXUN['imgurl']

# ----------------------------------------------------------

# ---------------------执行方法------------------------------

def clickCaixun(self):
"""
点击底部导航的财讯
:return:None
"""

self.find_element(*self.native_caixun).click()
time.sleep(2)

def get_screen(self, name):
"""
对当前屏幕截图,函数中调用了getScreenshot,在上方定义常量的时候必须定义img_url
:param name: 保存图片的名称
:return:None
"""

self.getScreenshot(name, self.img_url)

def clickTuijian(self):
"""
点击推荐
:return: None
"""

self.find_element(*self.tuijian).click()

def clickGupiao(self):
"""
点击股票
:return: None
"""

self.find_element(*self.gupiao).click()

测试步骤

class CaixunTest(AppTestCase, Caixun):

def testAClickTitle(self):
"""测试财讯的频道内容是否正确"""
self.clickCaixun()
self.assertEqual(self.get_title(), u'财讯')
self.clickTuijian()
self.get_screen('tuijian')
self.clickNeirong()
self.get_screen('tuijianneirong')
self.sysback()
self.clickGupiao()
self.get_screen('gupiao')
self.clickNeirong()
self.get_screen('gupiaoneirong')
self.sysback()
self.clickJijin()
self.get_screen('jijin')
self.clickNeirong()
self.get_screen('jijinneirong')
self.sysback()
self.clickZhaiquan()
self.get_screen('zhaiquan')
self.clickNeirong()
self.get_screen('zhaiquanneirong')
self.sysback()
self.clickXinsanban()
self.get_screen('xinsanban')
self.clickNeirong()
self.get_screen('xinsanbanneirong')

def testBAddPindao(self):
"""测试增加频道功能"""
self.clickCaixun()
self.clickPlus()
self.assertEqual(self.get_title(), u'频道管理')
# 校验推荐不可点击
self.assertFalse(self.checkTuijianEnabled())
# 校验其他可以点击
self.assertTrue(self.checkOtherAbled())

text = self.clickTextItem()
self.assertEqual(text, self.getFifthItemText())
self.clickItem(5)

def testCNewPindao(self):
"""测试新增频道内容显示"""
self.clickCaixun()
self.clickPlus()
text = self.clickTextItem()
self.sysback()
ori = self.getLocation(*config.CAIXUN['xinsanban'])
new = self.getLocation(*config.CAIXUN['tuijian'])
self.driver.swipe(ori[0], ori[1], new[0], new[1])
self.find_element(*(By.NAME, text)).click()
self.clickNeirong()
self.get_screen(text)

困惑

现在最大的困惑就是再怎么走下去,和一个朋友聊的时候他说这个迷茫期是很正常的,说明处在一个比较正常的十字路口,但是下一步怎么走他也没说。

我说说几个比较明确的疑问吧:

  1. 我这种设计方式有哪些比较严重的缺陷?(我自己感觉除了抽离出元素,好像和线性撸代码差别不是特别的大)
  2. 我想过关键字驱动的方法,但是步骤中有很多地方涉及到流程的控制,我没太明白这一块要怎么去设计
  3. 关于Android的性能方面要如何提升?参考群里的优质帖子,我自己也写了一个监控工具:《AndroidTools》,但是具体这些指标都是死的,不太明白哪些场景应该用哪些手段来测试哪些指标。
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 14 条回复 时间 点赞

UI自动化做的不多,但是只从你最后的用例代码来看,我只看到了一堆截图代码,可读性太差了

1、如果非得每个点击都执行截图操作,你应该实现一个自动截图的装饰器
2、你的pageobject不够抽象,感觉还是过程式的代码
3、可以增加Action层,把常用的点击流程封装起来
4、不要把时间浪费在关键字驱动上面

#1楼 @jacexh 刚好那部分需要用截图处理,其他部分还没传到github上

def testJiangXu(self):
"""测试自选页面降序排列"""
num = self.getLength() - 1
self.clickNameNum()
a = 999999
for x in range(num):
try:
int(self.getStockNum(x))
self.assertTrue(int(a) > int(self.getStockNum(x)))
a = self.getStockNum(x)
except ValueError:
pass

def testShengXu(self):
"""测试自选页面升序排列"""
num = self.getLength() - 1
self.clickNameNum()
self.clickNameNum()
a = 0
for x in range(num):
try:
int(self.getStockNum(x))
self.assertTrue(int(a) < int(self.getStockNum(x)))
a = self.getStockNum(x)
except ValueError:
pass

我自己的代码大概是这样的。

2、你的pageobject不够抽象,感觉还是过程式的代码

我也是这么觉得的,所以才想请教一下这部分要怎么处理

3、可以增加Action层,把常用的点击流程封装起来

这部分我是这么处理的

def clickHangQing(self):
"""
点击导航栏的行情按钮
:return: None
"""

self.find_element(*self.native_hangqing).click()

def clickEditSelect(self):
"""
点击编辑自选,进入编辑自选页面
:return: None
"""

self.find_elements(*self.edit_select)[0].click()

def clickFindStock(self):
"""
点击放大镜,进入查询自选股界面
:return: None
"""

self.find_elements(*self.edit_select)[1].click()

def clickZiXuan(self):
"""
点击行情页面的自选
:return: None
"""

if self.get_title() != u'行情':
try:
self.clickHangQing()
except Exception as e:
print e
print '行情导航栏无法点击,请检查APP的状态'
else:
self.find_element(*self.zixuan).click()

但是感觉耦合性还是比较差。

我感觉脚本与脚本之间存在复用,比如财讯有5个测试用例,那么这几个脚本之间存在复用。
可以再抽象一些方法放入page class

好高深啊,新手看不懂啊。我想请教个问题

#3楼 @pacerron 我想想要怎么修改,现在每一个测试用例都是单独的,除了调用基类的东西基本上没有做复用,就像
#1楼 @jacexh 说的那样,代码写的还不够抽象

新手看起来挺明白的 希望能早日赶上楼主的进度 共勉

#4楼 @abcfleeting 你倒是说呀,我这强迫症看着好难受

我给一个思路,设计一个pythonGUI,然后将单个操作分开,通过GUI,设置参数,来控制操作组合!

#9楼 @qwerty 不好意思我没太明白,是指用GUI来设置所有元素的参数吗?

#10楼 @wyb199026 我举个栗子,我这里有一个修改资料类,有八个可修改内容,假设我现在就需要修改两个,{‘name’:'qwerty','sex':'male'},将字典传入修改资料类,启动start方法进行这两个内容的修改,name对应哪个控件,sex又对应哪个控件,这个再配置一下

#11楼 @qwerty 明白了,非常感谢

谢谢楼主!看完之后很有启发!

点点寒彬 回复

楼主还在吗,可以交流下吗?

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册