Appium 关于 appium 的原生控件的 xpath 定位问题及常用方法

xiaoqi_11 · 2017年01月16日 · 最后由 enyo 回复于 2018年05月31日 · 2519 次阅读

关于类似的帖子好像很多,但是没有找到具体能帮我解决问题的办法。还是自己深究了好久才基本知道 app 上面的 xpath 定位和 web 上的不同点:

先放一个图:

第一,appium1.5 及之后的版本废弃了 name 属性 (如 name=账单,将不被支持用于定位),所以基本的定位就用下 id 就好了。其他的不多说了。

第二,下面就来说一下关于 xpath 的定位。主要场景为没有 id 或者没有 text,或者 text 是一个不可控的值(或者叫会发生变化的值,就比如 text 字段为 10 元,可能这个 10 每次
会变)的时候。其实简单点就是按路径定位包括一级或者多级路径。顺便说一下,路径方式分两种,一种是绝对路径(以第一个标签为参照物),另一种是相对路径
(已其他已知的标签为参照物),且在定位的时候尽量采用相对路径的方式。

1,先说说有 id 或者 text 的场景使用 xpath 的情况。(有 id 或者 name 为什么不直接用?以下均为相对路径)

上面说的 name 被废弃了,但是 xpath 的写法如//android.widget.TextView[@text="账单"] 是被支持的。

就比如上面的"账单"和"我要"的 id 都是 com.wlqq:id/title_left_btn,并且假设当前页面只有这两个位置 id 为前面写的,那么你在用 id 定位"账单"的时候,就可以用 xpath 了,因为 id 已经不唯一了。用 id 定位 “账单” 的为:xpath=(//android.widget.TextView[@resource-id="com.wlqq:id/title_left_btn"])[1],定位"我要"的为:
xpath=(//android.widget.TextView[@resource-id="com.wlqq:id/title_left_btn"])[2]
此处注意三点:
a,下标是从 1 开始,而不是 0;
b,如果有下标,需要用括号把前面的部分括起来,并且前面需要加 xpath=,可能有些人习惯了前面都加 xpath=,但是像我这种只习惯写//开头,不写 xpath=的就被坑惨了。。。反正不容易发现是因为没有写 xpath=,也可能是我个人比较坑吧。
c,就是和 web 不一样的就是标签的取值,在这里取的是 class 的值=android.widget.TextView 而不是看到的标签 TextView,具体原因没有深究。反正记住用 class 代替标签就对了。
另外,上面的只是为了说明只有 1 个层级的时候 xpath 的用法,1 层也算是一种相对路径吧。因为没有从第一个位置的属性开始写。xpath 的书写规则基本是越少越好。所以层级也是越少越好。有 1 层可以唯一定位就不要 2 层。 可能有点废话了。

2,现在就来说说没有 id 或者 name 的场景。 先来一张图:

现在有一个场景就是我需要点击上面那个小人图标,但是他没有 id 和 text 属性。能想到的办法就是下面要讲的 xpath 了。
用绝对路径的写法就是:如果图上的第一个是最顶上的话,就是
这样的,也就是需要 7 个层级,依次写下来就是:
//android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.RelativeLayout/android.widget.RelativeLayout/android.widget.LinearLayout[2]/android.widget.ImageButton
这种写法注意一下几点:
a , [2] 注意是 2 而不是 3,因为与标签的值有关。只有 2 个 LinearLayout。
b , 路径长度偏长,而且因为只有 class 的值,对于一些页面控件较多的,可能不止一个,也就是可能这种写法也都不是唯一。
c , 绝对路径基本很少使用,如果人品太差,遇到页面全是没有 id 或者 name 的,那就没办法了。或者考虑一些坐标。

3,(重要) 没有 id 或者 name 的场景下使用相对路径的办法来定位。主要介绍一下层级关系中的父子关系(上下级)和兄弟关系。
    大家可以看到,这个图里面有一个唯一的中文词汇--"钱包"。我们可以通过这个钱包来定位我们的小人图片。先分析下位置关系
。找找关系也就是如图所示,小人图标 3 是钱包 1 的弟弟 2LinearLayout 标签的儿子 ImageButton。儿子好理解,xpath 的层级关系也就是父子关系用/表示。//android.widget.LinearLayout/android.widget.ImageButton 这样就能表示弟弟的儿子了。但是现在问题是怎么表示钱包的弟弟?xpath 里面有一个轴,简单点可以理解为一个函数吧。我这样认为的。preceding-sibling:: 可以找到节点前面也就是哥哥节点,following-sibling::可以找到节点后面也就是弟弟节点,关于轴的更多用法啊,可以自行百度 xpath 的语法。这里还有一个用的多的就是 parent:: ,可以找到节点的父亲节点。但是父亲节点可以用..表示。下面就来具体说一下怎么用:
    基本知识已经介绍到此了。那么这里的定位方法就是上图中的 3 个层级://android.widget.TextView[@text="钱包"]/following-sibling::android.widget.LinearLayout/android.widget.ImageButton。 第一级就同前面说的唯一的找到钱包这个位置,后面的一级就是钱包的弟弟,也就是 following-sibling::android.widget.LinearLayout。当然注意因为是紧挨着的,所以弟弟没有下班,可想而知如果是第几个弟弟,就加个下标吧。哥哥也是同理。
前面用到了兄弟的关系,下面说一下儿子与父亲的关系。父子关系还是用图来说明
。我们的钱包 1 的父亲 2 有一个儿子 3 的儿子 4 就是我们的小人图标。这就是找关系。关系找到了,那我们就可以用这个关系来写 xpath 了。也就是钱包(//android.widget.TextView[@text="钱包"])的父亲(/parent::android.widget.RelativeLayout )的第二个 class=android.widget.LinearLayout 的儿子(/android.widget.LinearLayout[2])的儿子(小人/android.widget.ImageButton),好,我们连起来就是://android.widget.TextView[@text="钱包"]/parent::android.widget.RelativeLayout/android.widget.LinearLayout[2]/android.widget.ImageButton。顺便说一下父亲这个位置可以用..来代替,相比很多人都知道..在路径里面指的就是上级。所以可以用//android.widget.TextView[@text="钱包"]/../android.widget.LinearLayout[2]/android.widget.ImageButton 这个来代替上面的写法。
  注:最后再强调下,关于这个地方,下标为什么是 [2],是因为只与 class 相同的有关。钱包的 class 不一样。所以它就不算了。
   关于相对路径的父子关系,以及兄弟关系,相比大家应该有所体会了吧。如果还是没太懂,咱们再来个复杂点的例子。可能只是举例说明下语法。实际下面的可能不会这样复杂的写。先上图:
   假设我们需要通过加入购物车这个位置来定位我们的立即定位按钮,那么,我们的一种写法就是图上的这个关系 7 层级。也就是加入购物车 7(//android.widget.TextView[@text="加入购物车"]) 的父亲 1(/..)的父亲 2(/..)的父亲 3(/..) 的第二个兄弟 4(/following-sibling::android.view.View[2])的儿子 5(/android.view.View) 的儿子 6(也就是我们的立即购买/android.widget.TextView),连起来就是
    //android.widget.TextView[@text="加入购物车"]/../../../following-sibling::android.view.View[2]/android.view.View/android.widget.TextView。
注意:使用 text 的时候避免使用输入框的默认输入值,因为当你真实输入值之后,就没有这个 text 了,也就找不到路径了。另外也可以用模糊匹配,xpath 有一个 contains 函数。用法//android.widget.TextView[contains(@text,"购物车")].也能找到 “加入购物车” 这个位置。自行体会去吧。。。
  最后不知道你是否有看懂?欢迎各位朋友留言指正。在下也喜欢技术交流。

共收到 18 条回复 时间 点赞

点个赞,分享是种美德😁

—— 来自 TesterHome 官方 安卓客户端

看起来有点乱

感谢分享,个人建议还是主次分明好点,也就是不好的简单带过,重点介绍像 XPath 的相对路径你认为好的,id,name
等无法定位个人一般用 XPath 的相对路径,双属性,及包含关系来定位元素

#4 楼 @automation 谢谢你的建议,后面再改改。。

可读性太不好了

马上改改,的确太乱了

看的好乱啊,可以 xpath 语句用代码形式写吗?

#10 楼 @wenshi11019 不太明白您说的代码形式是啥意思,xpath 语句可以用在代码里面呀

最近也在做相关工作,帮助很大谢谢分享👍

#12 楼 @spanthrive 相互学习

谢谢 xiaoqi,前面没用 appium 做个安卓自动化,除了 id 与 text,都不知道元素怎么定位,很有用,谢谢了,这里的小写 view 是不是应该是 widget,这里没看明白

#14 楼 @xiangxiang view 对应的 class 值就好

—— 来自 TesterHome 官方 安卓客户端

很棒,看懂了!

谢谢分享,学习了

多谢分享,正遇到这种 既没有 id 又没有 text 的元素的定位
版主的这篇方法说明正好帮我我一个大忙!!
感谢~

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