@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
由于官方的原因废弃掉,所以不在这里赘述了。tagName
、linkText
和 partialLinkText
在我的理解中,一般是在 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 框架原声不支持,定位速度很慢,所以官方现在不推荐大家使用,也有其他替代的定位方式可使用。
-
使用绝对路径定位,如截图所显示的 xpath 路径
MobileBy.xpath("className/className/className/className")
-
使用相对路径定位
MobileBy.xpath("//className")
-
通过元素的索引定位
MobileBy.xpath("//className[index]")
-
通过元素的属性定位
MobileBy.xpath("//className[@label='更多信息']") # 使用一种属性定位 MobileBy.xpath("//className[@label='更多信息'][@isVisible='1']") # 使用两种属性定位 MobileBy.xpath("//className[contains(@label,'更多')]") # 使用部分属性定位(最强大)
1.4 AccessibilityId
替代以前的name
定位方式,推荐使用。
在 Android 上,主要使用元素的content-desc属性
,如该属性为空,不能使用此定位方式。
在 iOS 上,主要使用元素的label
或name
(两个属性的值都一样)属性进行定位,如该属性为空,如该属性为空,也是不能使用该属性。
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 的登录页面中,手机号码输入框和密码输入框两个元素信息如下图所示。
手机号码输入框和密码输入框大部分属性都一致,单独使用 className 和 id 已经不可行了。可能有同学问,输入框不是有默认文案(text 属性)吗?使用text 属性定位就可以啦。
在初始登录时,使用这个text 属性定位的确可以,但如果不是初始登录,手机号码输入框残留上次登录的手机号码,text 属性就变成上次登录的手机号,如 图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 中所得到的感悟,如果大家有更好的方法,也可以说出来,大家一起讨论,一起进步!