Appium+python自动化(十一)- 元素定位秘籍助你打通任督二脉 - 下卷(超详解)

2019-07-08 18:41:34 浏览数 (1)

简介

  宏哥看你骨骼惊奇,印堂发亮,必是练武之奇才! 按照上一篇的节目预告,这一篇还是继续由宏哥给小伙伴们分享元素定位,是不是按照上一篇的秘籍修炼,是不是感觉到头顶盖好像被掀开,内气从头上冒出去,顿时觉得整个身体都融化了,而且身轻如燕啊!而且控制不住手,想要动手操作一番呢?那还在等什么呢,和宏哥一起练起来吧!!!

1、 List定位

  List故名思义就是一个列表,在python里面也有list这一个说法,如果你不是很理解什么是list,这里暂且理解为一个数组或者说一个集合。首先一个list是一个集合,那么他的个数也就成了不确定性,所以这里需要用复数,所以在我们定位时我们不能够接着用find_element_by_id等等定位方式了,我们需要用他的复数形式find_elements_by_id,所有的定位方式都一样需要采用复数加s。这里我们接着上篇的案例讲,如何使用list定位想定位的元素。首先看一下图片:

我们查看图片可以知道我们能够很轻松的通过id定位到整个祖父节点,我们接下来需要做的事定位这个祖父节点下所有的“android.widget.RelativeLayout”父节点,同样的首先我们看一张图:

这里我们需要直接使用定位复数的方法来操作,直接看代码(祖父节点定位到父节点):

代码语言:javascript复制
element= driver.find_element_by_id("com.taobao.taobao:id/rv_main_container")
elements = element.find_elements_by_class_name("android.widget.FrameLayout")

通过上面的代码我们直接定位了com.taobao.taobao:id/rv_main_container父节点下的所有android.widget.FrameLayout子节点,但是由于这个android.widget.FrameLayout子节点下边还有许多相同的android.widget.LinearLayout孙节点。

这里我们需要直接使用定位复数的方法来操作,直接看代码(父节点定位到孙节点):

代码语言:javascript复制
1 elements = element.find_elements_by_class_name("android.widget.FrameLayout")
2 elements1 = elements[1].find_elements_by_class_name("android.widget.LinearLayout")

现在我们需要怎么去操作这个子节点了,这里有两种方法:

1、前面我们讲了List你可以理解为一个数组或者一个集合,这里定位的所有子节点最后就成了个list,如果我们要访问这个list里面的某一个元素我们可以像访问数组中的数据一样通过下标访问。最后的代码就是下面这个样子:

代码语言:javascript复制
1 element= driver.find_element_by_id("com.taobao.taobao:id/rv_main_container")
2 elements = element.find_elements_by_class_name("android.widget.FrameLayout")
3 elements1 = elements[1].find_elements_by_class_name("android.widget.LinearLayout")
4 elements1[1].click()

上面的代码最后的结果是选择了“聚划算”这个标签页面,然后点击进入。

备注:如果初学者不理解是如何通过下标访问的,这里说一下下标是从0开始,如果要访问list i中的第一个元素结果就是i[0],这部分知识可以看一下python基础。

2、如果你要访问List里面的元素,那么我们是否可以通过for循环语句来依次访问呢?这个在自动化中会经常用到。下面你可以通过这个思路自己去实战一下,看能否达到预期效果。下面看我的代码:

代码语言:javascript复制
1 element= driver.find_element_by_id("com.taobao.taobao:id/rv_main_container")
2 elements = element.find_elements_by_class_name("android.widget.FrameLayout")
3 elements1 = elements[1].find_elements_by_class_name("android.widget.LinearLayout")
4 for ele in elements1:
5     ele.click()

看上面的代码,我们通过循环去访问这个list里面的每一个元素,因为每次循环得到的都是其中一个元素,那么我们只需要在这个元素上加上你想要的操作即可,所以我们这里可以直接点击进去。

如果你动手做到这里会发现一个问题,你进入到第一个标签后没一会儿系统就会报错,为什么呢?你也可以试着去解决这个问题,后面我们会讲解这块儿知识。

2、 内嵌H5定位

2.1 hybrid定位思考

在web自动化中我们会遇见frame的问题,在遇见这些内嵌的标签后我们需要做的就是切换窗口,那么在app自动化测试也有类似的情况就是我们经常看见的内嵌html,在我们原生的app中增加一个由html做成的页面。大家可以思考一下这种情况怎么操作。

2.2 hybrid常见定位问题分析

首先我们看一下下面一张图片:

通过右边的结构图我们能够清晰的看见整个页面就是一个webview,无论从什么角度来定位我们都不能够很好的进行,如果这个时候我们需要操作页面的元素就需要通过切换contexts来完成。但是在讲这个知识点之前大家先按照网上的知识来试一下处理这个页面,看能否成功。下面先说大家会遇见的问题:

1、可能你看到有的文章显示我们不需要通过切换contexts就能够完成定位,这样的情况有,但是那种情况作者只在微博登录、qq登录等第三方登录时遇见过,如果不是这样的情况而像上面的情况就没办法通过类似的方法进行完成,所以我希望读者遇见这种情况时自己动手去操作,看什么方式更加适合自己的项目。

2、需要切换contexts那么就需要获取页面的所有contexts,此时你通过官网或者其他文章的知识通过下面的方法来获取,可能会报错,这种情况关系不大。

代码语言:javascript复制
1 webview = self.driver.contexts
2 print webview

如果你通过上面的代码来调试但是却报错,但是其他资料却没问题时你也不要着急,这里你需要确定两件事情:(1)、app打包的时候需要开启webview 的debug属性setWebContentDebuggingEnabled(true),这个直接让开发加上就好。一般情况是开启的,毕竟他们也要调试。(2)、你用很多手机去调试,发现有一些可以有一些不可以,但是你用模拟器却都可以,根据官方给出的答案是这个时候你需要去将手机root,然后再试。目前作者遇见了这两种情况,第二种我也是调试了很久才找到原因。

2.3 hybrid定位讲解

这两个问题解决后那么定位webview就轻松搞定,直接看代码:

代码语言:javascript复制
1 webview = driver.contexts
2 driver.switch_to.context(webview[1])
3 driver.find_element_by_link_text('PHP').click()

对于初学者对于上面的代码可能不是很理解,下面我们看一下日志:

大家这里不用管我执行代码和之前的区别(多了一个self),我们看下面控制台的输出,输出的是一个list,前面说过list和数组类似,在这个list里面有两个元素“NATIVE_APP”,“WEBVIEW_cn_com_open_mooc”,第一个元素是我们原生的app的contexts,后面的则是我们的webview的context,所以我们需要获取webview的context时只需要通过这个list的下表来进行访问。

我们获取到webview的context后只需要通过driver.switch_to.context()进行切换就好。当切换后我们就可以像定位web一样进行定位。

看下面一张图片我们通过浏览器将h5页面打开:

通过上面的图片我们就能够很轻松的像web一样进行定位,也就可以使用web的一些定位方式。看到这里是不是觉得解决了一个难题呢?动手去吧。

2.4 hybrid问题实战

通过前面的学习我相信你已经有了一些实战能力,这里给大家提一个问题,我们获取到的contexts每次一定是两个吗?如果不是两个那么我们上面的脚本是不是就没办法用了呢?可以思考一下这里怎么解决,在看我们下面的思路以及解决方案。

无论在什么页面我们去获取contexts时他无论有几个但是他的类型是不是肯定都是一个list呢?既然是list那么我们是否可以取到里面的每一个值,然后把每一个值进行判断,只要找出我们的webview就可以了呢?下面看代码:

代码语言:javascript复制
1 #获取当前页面所有的contexts
2 webview = driver.contexts
3 #在获取到的contexts list里面去挨个循环
4 for context in webview:
5   #判断循环中单个的context是否是webview,如果是就进行切换,并且跳出循环
6   if 'WEBVIEW' in context:
7     driver.switch_to.context(context)
8     break
9 driver.find_element_by_link_text('PHP').click()

通过上面的代码我们是否完美的解决了内嵌H5的定位问题呢?动手吧

3、 滑动定位

3.1 滑动定位方式

在app自动化中我们经常会遇见一个问题,我们需要查找的元素不在当前可展示的屏幕,至于在什么地方我们不知道,如果这个时候我们一直使用在当前页面查找,那么系统就会报错,为了解决这个问题我们就需要使用滑动查找。

首先的思路是我们在需要查找对象的页面查找一下该元素,判断该元素是否在当前页面,如果该元素不在该页面那么我们就需要去互动屏幕,到我们的下一屏幕,然后再进行查找,依次类推到找到为止。

3.2 滑动定位思路分析

方式我们有了,那么我们就需要知道实现这个功能应该有哪些点。下面跟着我一起来分析一下:

1、需要查找的元素我们是不是需要知道是什么呢?这个需要先确定

2、我们需要找的页面是在我们的当前页面的上方还是下方还是左方还是右方,我们不能确定,那么我们是否需要确定我们需要滑动的方向?

3、元素和方向有了,但是你知道我们每次需要滑动屏幕的多少吗?那么我们是否需要先去获取屏幕的大小,然后针对不同的方向去计算一个滑动的值呢?

万事具备只欠东风,去按照这个思路动手练习一吧。

3.3 滑动定位实战

一、根据上面的思路我们能首先来确定我们需要查找的元素,看下面图片:

我们要找实战推荐后面的“换一换”按钮,然后进行点击。首先我们查看他的定位信息

最后我们查找元素的定位信息代码如下:

代码语言:javascript复制
1 self.driver.find_element_by_id('cn.com.open.mooc:id/tv_replace').click()

  对于有一定基础的人可能会觉得这个很low,但是有没有思考过一个问题,我们可以通过这个代码去执行,在没有这按钮的时候却会报错,也就没有办法执行下去了,那么需要怎么处理呢?所以这个时候我们需要有一些python的容错知识,即使我们的代码执行出错了,那么也要让他按照我们的意思执行下去。try.......except.......,这个就是我们python中的容错处理 ,下面我们看添加后的代码:

代码语言:javascript复制
1 try:
2 self.driver.find_element_by_id('cn.com.open.mooc:id/tv_replace').click()
3 except Exception,e:
4 print e

try的意思就是告诉编译器试着去执行他下面这一段代码,如果报错了,那么你就把except里面的错误信息打印出来。

二、有了元素现在我们需要知道的是不是就是该怎么滑动界面了呢?首先我们看一下下面这张图片:

在我们使用app的过程中存在上面几种滑动情况,我们把整个界面看作为一个坐标系(x,y),如果我们需要往上滑动,那么我们是不是就是x轴不动,y轴从下往上动呢?往下就是x轴不动,y轴从上往下呢?同理左右滑动是不是就是应该y轴不动x轴左右滑动呢?可以好好去体会一下,脑海中有个画面。

在appium中滑动我们所需要使用的方法就是swipe函数,至于往哪个方向滑动就是看我们里面的x,y的值,如果我们需要下往上滑动那么我们就应该是:

代码语言:javascript复制
1 self.driver.swipe(x1,y1,x1,y2,t)

上面的代码x轴的值不变,y轴的值进行了变化,所以是沿着上下进行滑动的,从y2滑动到了y1点。t代表的是多少时间完成这个动作,或者说这个时间持续多久。

备注:这里需要注意的是屏幕的x,y的值是从左上角开始取的,左上角为(0,0),右下角是最大。

三、上面滑动的方法看着是好用,但是我们不可能每次都去填写一个坐标,那样太low,所以我们需要获取屏幕大小,直接看代码:

代码语言:javascript复制
1 x = self.driver.get_window_size()['width']
2 y = self.driver.get_window_size()['height']

  上面的代码就是我们获取到的x,y轴。通过思路我们的代码都有了,下面我们要做的就是对原来的代码进行修改,进行一个封装。下面看代码,这个暂时看不懂没关系,到后面我们学了python'基础就能够看懂了。先思路,然后了解。

代码语言:javascript复制
 1 #获取屏幕大小
 2          
 3 def getSize(self):
 4   x = self.driver.get_window_size()['width']
 5   y = self.driver.get_window_size()['height']
 6   return (x,y)
 7          
 8          
 9 #向左滑动
10 def swipeLeft(self,t):
11   l=self.getSize()
12   x1=int(l[0]*0.9)
13   y1=int(l[1]*0.5)
14   x2=int(l[0]*0.1)
15   self.driver.swipe(x1,y1,x2,y1,t)
16                
17 #向右滑动
18 def swipeRight(self,t):
19   l=self.getSize()
20   x1=int(l[0]*0.25)
21   y1=int(l[1]*0.5)
22   x2=int(l[0]*0.75)
23   self.driver.swipe(x1,y1,x2,y1,t)
24                
25 #向上滑动
26 def swipeUp(self,t):
27   l=self.getSize()
28   x1=int(l[0]*0.5)
29   y1=int(l[1]*0.8)
30   y2=int(l[1]*0.4)
31   self.driver.swipe(x1,y1,x1,y2,t)
32   time.sleep(5)
33          
34 #向下滑动
35 def swipeDown(self,t):
36   l=self.getSize()
37   x1=int(l[0]*0.5)
38   y1=int(l[1]*0.25)
39   y2=int(l[1]*0.75)
40   self.driver.swipe(x1,y1,x1,y2,t)
41          
42 #查找元素,没找到滑动
43 def findLocal(self):
44   x = 1
45   while x==1:
46     if self.fact() == 1:
47       self.swipeUp(2000)
48       time.sleep(3)
49       self.fact()
50     else:
51       print "找到了"
52       x=2
53              
54          
55          
56 #递归
57 def fact(self):
58   n =1
59   try:
60     self.driver.find_element_by_id('cn.com.open.mooc:id/tv_replace').click()
61   except Exception,e:
62     return n

  通过查看上面代码的整个逻辑就是1、首先去查找元素,如果找到了我就直接点击。2、如果没有找到元素那么我就往上滑动(这里可以自己选择),滑动后再次进行查找,如果找到就点击,没有找到继续滑动。动手动手,这里知识点很重要!虽然后面会有一些替代方法,但是思路、算法很重要。

4、小结

好了,元素定位,常见的大致就这些,这个目前就分享到这里吧,以后如果遇到,宏哥给小伙伴再补上!!!

0 人点赞