高阶定位技巧 - Xpath
大家好,今天我们要深入学习 XPath 定位的高阶技巧。XPath 是一种非常强大的工具,用来在 XML 或者 HTML 文档中精确定位节点。它能够帮助我们通过路径表达式来选取特定的元素。在自动化测试中,尤其是 Web 和 App 自动化测试中,XPath 是我们常用的定位方式之一。
简介
XPath 是一种用于在 XML 文档中定位和选择节点的语言。它可以通过使用路径表达式来指定节点的位置,并支持使用各种条件进行过滤和匹配。
以下是一些常见的 XPath 高阶定位方法:
-
使用逻辑运算符,如 and、or、not,将多个条件组合起来进行定位。
-
使用轴定位,通过预定义的轴(如子节点、父节点、兄弟节点等)来获取相对于当前节点的其他节点集合。
XPath 通过路径表达式定位节点,它允许我们通过不同的条件和运算符来精确查找我们需要的元素。你可以想象,它就像是一个导航工具,帮助你在层层嵌套的 HTML 或 XML 文档中,准确找到你想要的“目的地”。今天我们会讲到一些常见的高级定位方法,比如如何使用逻辑运算符,轴定位等,来实现更复杂的元素定位。
Xpath 高级定位技巧
现在我们就来聊聊一些常见的 XPath 高级定位技巧,首先是包含匹配。这个技巧非常实用,特别是当你不知道元素的完整属性值时。
包含
contains()
是 Xpath
表达式中的一个函数。contains 会匹配符合某属性中包含 xx 字符串的元素。例如//*[contains(@text,"hogwarts")]
则会匹配text
属性的属性值中包含hogwarts
的元素。
contains()
函数的使用格式://*[contains(@属性,"属性值")]
注意:
contains()
函数定位到的元素很可能是多个。contains()
函数内的属性名需要用@
开始。
contains 是 XPath 中的一个函数,它可以帮助我们匹配包含某个字符串的元素。例如,如果我们要定位所有文本中包含“阿里”的元素,就可以使用 //*[contains(@text,"阿里")]。它能够匹配所有 text 属性中包含“阿里”的元素,特别适合用在动态内容中,因为有时候元素的文本是变化的,或者我们只知道部分文本。记住,contains 定位可能会找到多个匹配的元素。所以在使用时,要确保你清楚自己想定位的元素到底是哪个。
示例
- 打开雪球 apk,在搜索框中输入
阿里
,界面如下:
假设我们打开雪球 APK,想要在搜索框中输入“阿里”,并且定位到相关的元素。
- 打开并连接元素定位工具,示例使用的工具
Appium Inspector
,点击如下图所示的搜索图标。
打开并连接元素定位工具,示例使用的工具 Appium Inspector,点击搜索图标。
- 定位策略选择
XPATH
,定位当前界面中text
属性包含阿里
的元素。//*[contains(@text,"阿里")]
选择 XPath 定位方式,输入定位表达式。
- 点击查找后,呈现共有 12 个元素符合
text
属性中包含阿里
,同时也符合之前提到的定位的元素很容易有多个这一特点。
点击查找后,呈现共有 12 个元素符合 text
属性中包含阿里
,同时也符合之前提到的定位的元素很容易有多个这一特点。
XPath 轴定位
XPath 轴是 XPath 语言中的一个重要概念,它可以根据节点之间的关系来选择节点。XPath 轴定义了节点的一个集合,这个集合由满足特定条件的节点组成。
在轴定位中,可以通过一个节点,定位到当前的节点的兄弟节点、父节点、祖先节点等等。
接下来我们要讲的是 XPath 轴定位。轴定位是一种非常强大的功能,它可以帮助我们根据节点之间的关系来定位其他节点。例如,你可以通过当前节点来定位它的父节点、兄弟节点或者子节点,这样就能够更加灵活地处理元素定位。
父子关系 - 当前节点的父节点
- 定位到当前节点的父节点,格式如下:
/当前节点/..
/当前节点/parent::*
如果你想从一个元素定位到它的父节点,可以使用 .. 或 parent::*。这两者效果一样,都是用来定位当前节点的父节点。比如,如果我们已经定位到一个 text 为“阿里巴巴”的元素,那么我们可以通过这两个表达式定位到它的父节点。
- 示例:定位
text
属性为阿里巴巴的元素的父节点
假设我们已经定位到 text 为“阿里巴巴”的元素。
- 定位
text
属性为阿里巴巴的元素。//*[@text="阿里巴巴"]
首先,可以先使用文本定位的方式,找到文本值为阿里巴巴的元素。
- 再定位父节点,两个表达式都可以。
//*[@text="阿里巴巴"]/..
//*[@text="阿里巴巴"]/parent::*
接下来就可以使用 //[@text="阿里巴巴"]/.. 或 //[@text="阿里巴巴"]/parent::*,都可以帮助我们定位到它的父节点。通过这种方式,我们能够准确地找到与当前元素相关的上层结构。
父子关系 - 当前节点的子节点
- 定位到当前节点的子节点,格式如下:
/当前节点/child::*
/当前节点/*
接下来,如果你需要定位当前节点的子节点,可以使用 child:: 或 。这两种方式都可以用来定位当前节点的所有子节点。如果你知道当前节点的 resource-id,你可以先定位它,然后通过这两种方式找到它的子元素。
- 示例:定位
resource-id
属性为com.xueqiu.android:id/stock_layout
的元素的子节点
假设我们要定位 resource-id 为 com.xueqiu.android:id/stock_layout 的元素的子节点。
- 搜索
阿里巴巴
后,进入下面的页面,定位下图的元素。
定位顺序是这样的。首先搜索阿里巴巴后,进入下面的页面,定位下图的元素。
- 使用 resource-id 定位当前节点。
`//*[@resource-id="com.xueqiu.android:id/stock_layout"]``
然后使用 //*[@resource-id="com.xueqiu.android:id/stock_layout"] 先找到当前节点。
- 再定位当前节点的子节点,最终找到多个节点。
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/child::*
可以使用 //[@resource-id="com.xueqiu.android:id/stock_layout"]/child::。这样,你就可以快速找到它的子节点,进一步对页面结构进行分析和测试。
祖孙关系 - 当前节点的爷节点
从当前节点定位到父级节点的的父级节点,使用示例如下:
/当前节点/../..
/当前节点/parent::*/parent::*
如果你需要定位到当前节点的爷节点,可以使用 ../.. 或 parent::/parent::。这种方式可以帮助你找到比父节点更上层的节点。
- 示例:定位
resource-id
属性为com.xueqiu.android:id/stockName
的元素的爷节点
比如,我们想要定位到 resource-id 为 com.xueqiu.android:id/stockName 的元素的爷节点。
- 定位当前元素的爷节点
可以先定位当前元素的爷节点。
- 爷节点的定位如图
可以先分析一下,发现可以通过这个元素来找到想要的元素。能定位到的元素有一个很明确的 resource id 属性。
- 先定位到当前节点,再寻找两层的父节点。
//*[@resource-id="com.xueqiu.android:id/stockName"]/../..
要找它的爷节点,可以使用 //*[@resource-id="com.xueqiu.android:id/stockName"]/../..。这种定位方式能帮助我们更快速地了解页面结构,尤其在层次较深的页面中非常有用。
祖孙关系 - 当前节点的孙节点
从当前节点定位到子节点的子节点,使用示例为:
/当前节点/child::*/child::*
/当前节点/*/*
如果你想定位到当前节点的孙节点,可以使用 /child::/child:: 或 //。这样你可以找到当前节点的子节点的子节点,非常适合在复杂的层级结构中进行定位。
- 示例:定位
resource-id
属性为com.xueqiu.android:id/stock_layout
的元素的孙节点
假设我们定位到 resource-id 为 com.xueqiu.android:id/stock_layout 的元素,想要找它的孙节点。
- 定位当前节点的孙子节点
可以先定位当前节点的孙子节点。
- 先定位到当前节点,在定位两层的儿子节点,即可定位到孙子节点。
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/child::*/child::*
可以使用 //[@resource-id="com.xueqiu.android:id/stock_layout"]。然后,继续通过 /child:: 来定位孙子节点。
祖孙关系 - 当前节点的祖先节点
- 返回当前节点的所有祖先节点,使用示例为
/当前节点/ancestor::*
如果你想定位到当前节点的所有祖先节点,可以使用 ancestor::*。这种方式可以帮助你找到从当前节点向上追溯到根节点的所有元素。
- 示例:定位
text
属性为HK
的元素的 class 属性为某个值的所有祖先节点
假设我们定位到 text 为“HK”的元素,想要找到它所有的祖先节点,并过滤出某些特定的布局类型。
//*[@text="HK"]/ancestor::android.widget.LinearLayout
//*[@text="HK"]/ancestor::android.widget.RelativeLayout
示例中表示是找到元素包含文本 HK
并且它的所有祖先元素中属性 class
为 android.widget.LinearLayout
和 android.widget.RelativeLayout
的节点,其中这两个属性值分别是常用的线性布局类和相对布局类。
示例中表示是找到元素包含文本 HK 并且它的所有祖先元素中属性 class 为 android.widget.LinearLayout 和 android.widget.RelativeLayout 的节点,其中这两个属性值分别是常用的线性布局类和相对布局类。
- 显式指定要返回的祖先节点,使用方式为:
//*[@text="HK"]/ancestor::android.widget.RelativeLayout[1]
可以使用类似 //*[@text="HK"]/ancestor::android.widget.LinearLayout 的方式。这会返回包含 android.widget.LinearLayout 的所有祖先节点。
- 定位当前节点线性布局的祖先节点。
//*[@text="HK"]/ancestor::android.widget.LinearLayout
整体定位的顺序是这样的。首先定位当前节点线性布局的祖先节点。
- 一共定位到了 9 个祖先
可以看到一共定位到了 9 个祖先。
- 下一步定位这些祖先节点中距离当前节点最近的祖先节点,同时也是当前节点的父节点。
下一步定位这些祖先节点中距离当前节点最近的祖先节点,同时也是当前节点的父节点。
- 只需要在定位所有祖先的表达时候添加
[1]
即可。//*[@text="HK"]/ancestor::android.widget.LinearLayout[1]
最后只需要在定位所有祖先的表达时候添加 [1] 就可以了。
注意: 这里的[1]
并不是数组下标的意思,可以理解为,从当前定位的元素向前数一代,也可以理解为向外一层。
这里需要注意,这里的 [1] 并不是数组下标的意思,可以理解为,从当前定位的元素向前数一代,也可以理解为向外一层。
兄弟关系 - 当前节点的兄弟节点
- 定位当前节点后的所有兄弟节点
/当前节点/following-sibling::*
兄弟节点的定位也非常重要。你可以使用 following-sibling::* 来定位当前节点后的所有兄弟节点。
- 定位当前节点后的兄弟节点中的某一个节点,在定位所有兄弟节点后添加条件。
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/following-sibling::*[@resource-id="com.xueqiu.android:id/price_layout"]
例如,我们想要定位当前节点后面的兄弟节点,可以使用 //[@resource-id="com.xueqiu.android:id/stock_layout"]/following-sibling::。如果只需要某个特定的兄弟节点,可以在后面加上筛选条件。
- 定当前节点后的所有兄弟节点
整体的定位顺序是这样的。先定当前节点后的所有兄弟节点。
- 使用 XPath 定位,定位到的两个元素就是上图中的两个兄弟。
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/following-sibling::*
然后通过 following-sibling::* 定位到这个元素后面的所有兄弟节点。
- 当元素不只一个兄弟节点时,如果需要定位这些兄弟节点中的某一个,则需要再增加一个条件,在这些兄弟节点中定位到
resource-id
是com.xueqiu.android:id/price_layout
的节点。
//*[@resource-id="com.xueqiu.android:id/stock_layout"]/following-sibling::*[@resource-id="com.xueqiu.android:id/price_layout"]
当元素不只一个兄弟节点时,如果需要定位这些兄弟节点中的某一个,则需要再增加一个条件,在这些兄弟节点中定位到 resource-id 是 com.xueqiu.android:id/price_layout 的节点。
- 定位当前节点前的所有兄弟节点
/当前节点/preceding-sibling::*
还可以使用 preceding-sibling::* 来定位前面的兄弟节点。
- 节点前有多个兄弟节点
//*[@resource-id="com.xueqiu.android:id/add_attention"]/preceding-sibling::*[@resource-id="com.xueqiu.android:id/price_layout"]
比如当前节点之前有多个兄弟节点,通过这种方式就可以定位到。
- 定位当前节点前的所有兄弟节点
具体定位的顺序是这样的。首先先找到当前节点。
- 使用 Xpath 定位,表达式如下:
//*[@resource-id="com.xueqiu.android:id/add_attention"]/preceding-sibling::*
然后使用 preceding-sibling::* 找到这个节点之前所有的兄弟节点。
- 如果定位到的元素为单个时,可直接使用。如果定位到的兄弟节点有多个,定位到某一个兄弟节点同样需要增加过滤条件。例如,需要定位到兄弟节点中,
resource-id
为com.xueqiu.android:id/stock_layout
的元素。//*[@resource-id="com.xueqiu.android:id/add_attention"]/preceding-sibling::*[@resource-id="com.xueqiu.android:id/stock_layout"]
如果定位到的元素为单个时,可直接使用。如果定位到的兄弟节点有多个,定位到某一个兄弟节点同样需要增加过滤条件。例如,需要定位到兄弟节点中,resource-id 为 com.xueqiu.android:id/stock_layout 的元素。
XPath 运算符
AND
AND 表示可以在 XPath
表达式中同时具备 2 个条件,在 AND
两个条件都应该为真的情况下,即该元素既有 条件A
又有 条件B
。AND 定位取到的是交集。
XPath 中的运算符非常有用。其中 and 运算符要求两个条件都为真,才能选中元素。
- 示例:定位如下图页面中的红框所框出来的元素。
例如,我们可以通过 and 运算符来同时筛选 resource-id 和文本内容都符合的元素。
- 使用 resource-id 进行定位时,会定位到多个元素
可以先使用 resource-id 进行定位到多个元素。
- 使用
and
运算符增加筛选条件进行过滤,需要满足符合 resource-id,且文本内容为阿里巴巴
的元素。只有两个条件都符合时才会被选中。//*[@resource-id="com.xueqiu.android:id/stockName" and @text="阿里巴巴"]
然后使用 and 运算符增加筛选条件进行过滤,需要满足符合 resource-id,且文本内容为阿里巴巴的元素。只有两个条件都符合时才会被选中。
OR
OR 表示可以在 XPath
表达式中放置 2 个条件,在 OR
的情况下,两个条件中的任何一个为真,就可定位到该元素。OR
定位获取的是并集。
而 or 运算符则只要有一个条件满足,就会选中元素。通过运算符的组合,可以实现非常复杂的定位。
- 示例:定位当前页面中
resource-id
为com.xueqiu.android:id/stockName
或文本内容text
为加自选的元素
,也就是下面 6 个元素:
比如想要定位当前页面中 resource-id 为 com.xueqiu.android:id/stockName 或文本内容 text 为加自选的元素。
//*[@resource-id="com.xueqiu.android:id/stock_layout" or @text="加自选"]
- 定位结果如图所示:
就可以使用 or 运算符来筛选 resource-id 或文本内容符合的元素。
总结
- 包含
- XPath 轴定位
- XPath 运算符
今天我们学习了几个常见的 XPath 高阶定位技巧,包括包含匹配、轴定位、运算符应用等。这些技巧能帮助你更加精准地定位页面元素,尤其在页面结构复杂或者动态内容较多的情况下非常有用。希望大家在实际测试中能够灵活运用这些技巧,提升测试效率和准确性!