[贝聊科技]Appium 元素定位方式大揭秘

@DC_ing 贝聊 移动开发部 测试菜鸟

前言

相信大家在使用 Appium 时,都会遇到过一个问题,怎么更好地在一个页面中对某一个元素进行更快速的定位方式。本篇文章基于大家刚接触 Appium,对元素定位还是比较模糊。

Appium 定位方式是依赖于 Selenium 的。所以 Selenium 的定位方式,Appium 都支持,还加上Android 和 iOS 原生的定位方式。这样一下来,就有十多种定位方式,挑选哪一种使用,也是有些讲究的。

1. Appium 定位方式种类

目前,Appium 支持的定位方式,如下所示:

cssSelector             # Selenium 最强大的定位方法,比 xpath 速度快,但比 xpath 难上手
linkText                # 链接元素的全部显示文字
partialLinkText         # 链接元素的部分显示文字
name                    # 元素的 name 属性,目前官方在移动端去掉这个定位方式,使用 AccessibilityId 替代
tagName                 # 元素的标签名
className               # 元素的 class 属性
id                      # 元素的 id 属性
xpath                   # 比 css 定位方式稍弱一些的定位方法,但胜在容易上手,比较好使用,缺点就是速度慢一些。
AccessibilityId         # Appium 中用于替代 name 定位方式
AndroidUIAutomator      # Android 测试,最强大速度最快的定位方式
iOSNsPredicateString    # iOS 谓词的定位方式,仅支持 XCTest 框架,需大于 iOS 9.3或以上
IosUIAutomation         # iOS 谓词的定位方式,仅支持 UIAutomation 框架,需大于 iOS 9.3或以下
iOSClassChain           # 国外大神 Mykola Mokhnach 开发类似 xpath 的定位方式,仅支持  XCTest 框架,,不如 xpath 和 iOSNsPredicateString 好
windowsAutomation       # windows 应用自动化的定位方式

上述所示的定位方式中,name由于官方的原因废弃掉,所以不在这里赘述了。tagNamelinkTextpartialLinkText 在我的理解中,一般是在 web 页面使用,移动端很少用。

接下来,说一下我认为在 App UI自动化中常用到定位方式的详细用法。

1.1 className

使用元素的className属性定位,支持:Android 和 iOS,推荐使用。

MobileBy.className("XCUIElementTypeButton")

1.2 id

使用元素的resource-id属性定位,支持:Android,仅支持 Android 4.2或以上,推荐使用,一般来说,使用 id 能准确定位,就使用 id 吧,定位信息简洁,不容易错误。反正我没有在 iOS 用过,大家有正确使用过的例子,可以分享一下。

MobileBy.id("package.name:id/android")

1.3 xpath

支持:Android 和 iOS。但由于 iOS 10开始使用的 XCUITest 框架原声不支持,定位速度很慢,所以官方现在不推荐大家使用,也有其他替代的定位方式可使用。

  1. 使用绝对路径定位,如截图所显示的 xpath 路径

    MobileBy.xpath("className/className/className/className")
    
  2. 使用相对路径定位

    MobileBy.xpath("//className")
    
  3. 通过元素的索引定位

    MobileBy.xpath("//className[index]")
    
  4. 通过元素的属性定位

    MobileBy.xpath("//className[@label='更多信息']")    # 使用一种属性定位
    
    MobileBy.xpath("//className[@label='更多信息'][@isVisible='1']")    # 使用两种属性定位
    MobileBy.xpath("//className[contains(@label,'更多')]")    # 使用部分属性定位(最强大)
    

1.4 AccessibilityId

替代以前的name定位方式,推荐使用。
在 Android 上,主要使用元素的content-desc属性,如该属性为空,不能使用此定位方式。
在 iOS 上,主要使用元素的labelname(两个属性的值都一样)属性进行定位,如该属性为空,如该属性为空,也是不能使用该属性。

MobileBy.AccessibilityId("更多信息")

1.5 AndroidUIAutomator

仅支持 Android 4.2或以上,可支持元素的单个属性和多个属性定位,推荐使用。

支持元素以下属性定位:

index(int index)
text(String text)
resourceId(String id)
className(String className)
packageName(String packageName)
description(String desc)
checked(boolean val)
clickable(boolean val)
enabled(boolean val)
longClickable(boolean val)
password(boolean val)
selected(boolean val)
instance(int val)
# 其他一些详细方法(包括正则表达式匹配),请查看 Android 源码中,UiSelector 类定义的方法

例子:

MobileBy.AndroidUIAutomator("new UiSelector().text(\"发送\")")    # 使用一种属性定位
MobileBy.AndroidUIAutomator("new UiSelector().text(\"发送\").clickable(true)")    # 使用两种属性定位

元素的所有属性都可用做定位,功能非常强大,且速度很快。

1.6 iOSNsPredicate

仅支持 iOS 10或以上,可支持元素的单个属性和多个属性定位,推荐使用。详细请参照
iOSNsPredicate 定位。

MobileBy.iOSNsPredicateString("type == 'XCUIElementTypeButton'")    # 使用一种属性定位
MobileBy.iOSNsPredicateString("type == 'XCUIElementTypeButton' AND label == '更多信息'")    # 使用两种属性定位

具体 iOSNsPredicate 语法结构可查看官方文档,或查看我另外的一个帖子。

1.7 iOSClassChain

仅支持 iOS 10或以上,这是 github 的 Mykola Mokhnach 大神开发,仅限在 WebDriverAgent 框架使用,用于替代 xpath 的,但使用一阵子后,感觉灵活性没有 xpath 和 iOSNsPredicate 好,应该还不完善吧。具体使用方法,请见:iOSClassChain。

MobileBy.iOSClassChain('XCUIElementTypeWindow[1]/XCUIElementTypeOther[1]/XCUIElementTypeOther[1]/XCUIElementTypeNavigationBar[1]/XCUIElementTypeOther[1]/XCUIElementTypeButton[2]')

1.8 IosUIAutomation

仅支持 iOS 9.3或以下,是 iOS 旧框架 UIAutomation 的定位方式,这个定位类型同样可使用 iOS 谓词进行定位,详细可参考:iOSNsPredicate

MobileBy.IosUIAutomation("type == 'UIAButton'")     # 使用一种属性定位

MobileBy.IosUIAutomation("type == 'UIAButton' AND label == '更多信息'")    # 使用两种属性定位

2. 元素复杂定位

在贝聊家长版 Android 的登录页面中,手机号码输入框和密码输入框两个元素信息如下图所示。

[贝聊科技]Appium 元素定位方式大揭秘_第1张图片
图1
[贝聊科技]Appium 元素定位方式大揭秘_第2张图片
图2

手机号码输入框和密码输入框大部分属性都一致,单独使用 className 和 id 已经不可行了。可能有同学问,输入框不是有默认文案(text 属性)吗?使用text 属性定位就可以啦。
在初始登录时,使用这个text 属性定位的确可以,但如果不是初始登录,手机号码输入框残留上次登录的手机号码,text 属性就变成上次登录的手机号,如 图3 所示。

[贝聊科技]Appium 元素定位方式大揭秘_第3张图片
图3

在类似这样的输入框中,元素的 text 属性会不断变化。在进行元素定位时,我们会尽可能避免使用可变化值的属性进行定位。

由此可见,手机号码、密码两个输入框基本上不能使用单一属性进行定位,如果这样的话,我们可以使用元素多属性进行定位。观察图1和图2,两个元素的属性大部分一致,但还是有3个属性是不同的:focused、password、instance。再结合只有两个输入框相同的属性值,这样一定位,即可找到该元素了。

使用 AndroidUIAutomator 定位。UiSelector 不支持 password 属性定位。

# 手机号码输入框
MobileBy.AndroidUIAutomator("new UiSelector().resourceId(\"com.babychat:id/edit_content\").focused(true)")
MobileBy.AndroidUIAutomator("new UiSelector().className(\"android.widget.EditText\").instance(0)")

# 密码输入框
MobileBy.AndroidUIAutomator("new UiSelector().resourceId(\"com.babychat:id/edit_content\").focused(false)")
MobileBy.AndroidUIAutomator("new UiSelector().className(\"android.widget.EditText\").instance(1)")

使用 xpath 定位,不支持使用 instance 属性定位

# 手机号码输入框
MobileBy.xpath("//android.widget.EditText[@focused='true']")
MobileBy.xpath("//android.widget.EditText[@password='false']")

# 密码输入框
MobileBy.xpath("//android.widget.EditText[@focused='false']")
MobileBy.xpath("//android.widget.EditText[@password='true']")

3. 总结

Appium 元素的一些定位方式,大体上就上面跟大家所说的那样。只要能将你想要的元素定位到,具体使用哪个,就得看个人习惯了。但如果想要定位的元素,其属性与其他元素相似较多的话,就需要使用两种甚至三种属性进行定位了,具体使用哪些属性,就得跟其他属性一一对比,找出不相同的属性,根据属性来使用定位类型,这样比较靠谱一些。

本篇文章是本人在学习 Appium 中所得到的感悟,如果大家有更好的方法,也可以说出来,大家一起讨论,一起进步!

你可能感兴趣的:([贝聊科技]Appium 元素定位方式大揭秘)